1 Introduction

  • Objectives: using spatial data analysis methods to study the Life Expectancy across various countries, aiming to uncover patterns and disparities between groups of countries.

  • Potential analysis methods: Using Exploratory Data Analysis (Descriptive and Inference Analysis) and Machine Learning for Spatial Prediction.

2 Data Description

  • This dataset examines the geographic and socioeconomic factors influencing life ex- pectancy across various countries and years (from 2000 to 2015).

  • Source: Kaggle( link )

  • For spatial attribute (latitude and longitude), these value are merge from a countries coordinates dataset (link) based on Country name

  • Feature descriptions:

        library(knitr)
    library(kableExtra)
    
    # Create a data frame with key attributes
    key_attributes <- data.frame(
      Feature = c("Country", "Year", "Status", "Longitude", "Latitude", 
                  "Life expectancy", "Adult Mortality", "Infant deaths", 
                  "Alcohol", "Percentage expenditure", "Hepatitis B", 
                  "Measles", "BMI", "Under-five deaths", "Polio", 
                  "Total expenditure", "Diphtheria", "HIV/AIDS", "GDP", 
                  "Population", "Thinness 1-19 years", "Thinness 5-9 years", 
                  "Income composition of resources", "Schooling"),
      Description = c(
        "Name of the country",
        "Year of observation",
        "Urban or rural status",
        "Longitude for each country",
        "Latitude for each country",
        "Life expectancy at birth in years (average period that a person may expect to live)",
        "Probability of dying between 15 and 60 years per 1000",
        "Number of infant deaths per 1000 population",
        "Alcohol consumption, measured as liters per capita",
        "Expenditure on health as a percentage of GDP",
        "Hepatitis B immunization coverage among 1-year-olds (%)",
        "Number of reported measles cases per 1000 population",
        "Average Body Mass Index of the population",
        "Number of deaths under age five per 1000 population",
        "Polio immunization coverage among 1-year-olds (%)",
        "Total government health expenditure as a percentage of GDP",
        "Diphtheria tetanus toxoid and pertussis immunization coverage among 1-year-olds (%)",
        "Deaths per 1,000 live births due to HIV/AIDS (0-4 years)",
        "Gross Domestic Product per capita (in USD)",
        "Population of the country",
        "Prevalence of thinness among children and adolescents aged 10-19 (%)",
        "Prevalence of thinness among children aged 5-9 (%)",
        "Human Development Index in terms of income composition of resources (0 to 1)",
        "Number of years of schooling"
      )
    )
    
    # Display table
    kable(key_attributes, col.names = c("Feature", "Description"), caption = "Key Features") %>%
      kable_styling(bootstrap_options = c("striped", "hover", "bordered"), full_width = FALSE) %>%
      column_spec(1, bold = TRUE, border_right = TRUE) %>%
      column_spec(2, width = "60%") %>%
      row_spec(0, bold = TRUE, background = "#f7f7f7")

    Key Features

    Feature

    Description

    Country

    Name of the country

    Year

    Year of observation

    Status

    Urban or rural status

    Longitude

    Longitude for each country

    Latitude

    Latitude for each country

    Life expectancy

    Life expectancy at birth in years (average period that a person may expect to live)

    Adult Mortality

    Probability of dying between 15 and 60 years per 1000

    Infant deaths

    Number of infant deaths per 1000 population

    Alcohol

    Alcohol consumption, measured as liters per capita

    Percentage expenditure

    Expenditure on health as a percentage of GDP

    Hepatitis B

    Hepatitis B immunization coverage among 1-year-olds (%)

    Measles

    Number of reported measles cases per 1000 population

    BMI

    Average Body Mass Index of the population

    Under-five deaths

    Number of deaths under age five per 1000 population

    Polio

    Polio immunization coverage among 1-year-olds (%)

    Total expenditure

    Total government health expenditure as a percentage of GDP

    Diphtheria

    Diphtheria tetanus toxoid and pertussis immunization coverage among 1-year-olds (%)

    HIV/AIDS

    Deaths per 1,000 live births due to HIV/AIDS (0-4 years)

    GDP

    Gross Domestic Product per capita (in USD)

    Population

    Population of the country

    Thinness 1-19 years

    Prevalence of thinness among children and adolescents aged 10-19 (%)

    Thinness 5-9 years

    Prevalence of thinness among children aged 5-9 (%)

    Income composition of resources

    Human Development Index in terms of income composition of resources (0 to 1)

    Schooling

    Number of years of schooling

Load Libraries

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.1     ✔ tibble    3.2.1
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.0.4     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter()     masks stats::filter()
## ✖ dplyr::group_rows() masks kableExtra::group_rows()
## ✖ dplyr::lag()        masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(sf)
## Linking to GEOS 3.12.1, GDAL 3.8.4, PROJ 9.4.0; sf_use_s2() is TRUE
library(spdep)
## Loading required package: spData
## To access larger datasets in this package, install the spDataLarge
## package with: `install.packages('spDataLarge',
## repos='https://nowosad.github.io/drat/', type='source')`
library(GWmodel)
## Loading required package: robustbase
## Loading required package: sp
## Loading required package: Rcpp
## Welcome to GWmodel version 2.4-2.
library(tmap)
library(ggplot2)
library(dplyr)

2.1 Data Preprocessing

  • Checking for missing value within features:
# Read datasets
df <- read.csv("LifeExpectancy.csv")
coordinates <- read.csv("coordinate.csv")

# Merge datasets based on country name
df <- merge(df, coordinates[, c("name", "latitude", "longitude")], 
            by.x = "Country", by.y = "name", all.x = TRUE)
df$ID <- paste(df$Country, df$Year, sep = "_")

# Compute missing value counts and percentages
na_counts <- colSums(is.na(df[, !names(df) %in% c("ID")]))
total_counts <- nrow(df)
na_percentage <- (na_counts / total_counts) * 100  # Convert to percentage

# Convert to data frame for visualization
na_df <- data.frame(Feature = names(na_counts), NA_Count = na_counts, NA_Percentage = na_percentage)
pastel_colors <- c("#FBB4AE", "#B3CDE3", "#CCEBC5", "#DECBE4", "#FED9A6", "#FFFFCC", 
                   "#E5D8BD", "#FDDAEC", "#F2F2F2", "#A6D854", "#66C2A5", "#FC8D62", 
                   "#8DA0CB", "#E78AC3", "#FFD92F", "#A6CEE3", "#B2DF8A", "#FB9A99", 
                   "#CAB2D6", "#FFED6F", "#BC80BD", "#80B1D3", "#FFB3B3", "#BEBADA")
# Plot missing values as a horizontal bar chart with percentage
ggplot(na_df, aes(x = NA_Percentage, y = reorder(Feature, NA_Percentage), fill = Feature)) +
  geom_bar(stat = "identity", show.legend = FALSE) + 
  labs(title = "Missing Values of Features ",
       x = "Percentage of Missing Values (%)",
       y = "Feature") +
  scale_x_continuous(breaks = seq(0, 100, by = 5)) +  # Add x-axis ticks every 5%
  scale_fill_manual(values = pastel_colors) +
  theme_minimal() +
  theme(panel.grid.major = element_blank(),
        panel.background = element_rect(fill = "white"),
    axis.text.y = element_text(size = 10))  # Adjust text size for readability

  • Data Cleaning: to avoid error with missing data, we can remove them from the dataset.

Data size before cleaning:

print(dim(df))
## [1] 2928   25

Data size after cleaning:

df <- df %>% drop_na()  # Remove NA values
df$Country <- as.factor(df$Country) # Convert categorical variables
df$Status <- as.factor(df$Status) 
print(dim(df))
## [1] 1602   25

3 Exploratory Data Analysis

  • In this part, we focus on the key feature: Life Expectancy.

  • Methodology: doing descriptive analysis then inference analysis to have an overview understanding about the general trend of this feature –> Conclusion: which other features can have an effect on Life Expectancy.

3.0.1 Descriptive Analysis

3.0.1.1 Numerical Summary Measures

  • Using summary statistics to understand the general trends and giving quantitative insight.

    # Load necessary libraries
    library(dplyr)
    library(knitr)
    library(kableExtra)  # For enhanced table formatting
    
    # Compute summary statistics for Life Expectancy
    summary_table <- df %>%
      group_by(Status) %>%
      summarise(
        Count = n(),
        Mean = round(mean(Life.expectancy, na.rm = TRUE), 3),
        Standard_Deviation = round(sd(Life.expectancy, na.rm = TRUE), 3),
        Min = min(Life.expectancy, na.rm = TRUE),
        `Percentile 25%` = round(quantile(Life.expectancy, 0.25, na.rm = TRUE), 3),
        `Percentile 50%` = round(median(Life.expectancy, na.rm = TRUE), 3),
        `Percentile 75%` = round(quantile(Life.expectancy, 0.75, na.rm = TRUE), 3),
        Max = max(Life.expectancy, na.rm = TRUE)
      ) %>%
      bind_rows(
        df %>% summarise(
          Status = "All Countries",
          Count = n(),
          Mean = round(mean(Life.expectancy, na.rm = TRUE), 3),
          Standard_Deviation = round(sd(Life.expectancy, na.rm = TRUE), 3),
          Min = min(Life.expectancy, na.rm = TRUE),
          `Percentile 25%` = round(quantile(Life.expectancy, 0.25, na.rm = TRUE), 3),
          `Percentile 50%` = round(median(Life.expectancy, na.rm = TRUE), 3),
          `Percentile 75%` = round(quantile(Life.expectancy, 0.75, na.rm = TRUE), 3),
          Max = max(Life.expectancy, na.rm = TRUE)
        )
      )
    
    # Transpose the table
    summary_table_transposed <- as.data.frame(t(summary_table))
    
    # Rename columns with country status
    colnames(summary_table_transposed) <- summary_table_transposed[1, ]  # First row as column names
    summary_table_transposed <- summary_table_transposed[-1, ]  # Remove the first row
    
    # Print transposed summary table with better styling
    kable(summary_table_transposed, caption = "Summary Statistics of Life Expectancy") %>%
      kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE) %>%
      column_spec(1, border_right = TRUE, bold = TRUE) %>%  # Add vertical border and bold first column
      row_spec(0, bold = TRUE)  # Bold header row

    Summary Statistics of Life Expectancy

    Developed

    Developing

    All Countries

    Count

    242

    1360

    1602

    Mean

    78.692

    67.629

    69.300

    Standard_Deviation

    4.273

    8.465

    8.904

    Min

    69.9

    44.0

    44.0

    Percentile 25%

    75.550

    62.475

    64.200

    Percentile 50%

    78.95

    69.20

    71.70

    Percentile 75%

    81.4

    73.9

    75.0

    Max

    89

    89

    89

  • For other features:

summary(df)
##         Country          Year             Status     Life.expectancy
##  Afghanistan:  16   Min.   :2000   Developed : 242   Min.   :44.0   
##  Albania    :  16   1st Qu.:2005   Developing:1360   1st Qu.:64.2   
##  Armenia    :  15   Median :2008                     Median :71.7   
##  Austria    :  15   Mean   :2008                     Mean   :69.3   
##  Belarus    :  15   3rd Qu.:2011                     3rd Qu.:75.0   
##  Belgium    :  15   Max.   :2015                     Max.   :89.0   
##  (Other)    :1510                                                   
##  Adult.Mortality infant.deaths        Alcohol       percentage.expenditure
##  Min.   :  1.0   Min.   :   0.00   Min.   : 0.010   Min.   :    0.00      
##  1st Qu.: 76.0   1st Qu.:   1.00   1st Qu.: 0.730   1st Qu.:   37.84      
##  Median :149.0   Median :   3.00   Median : 3.710   Median :  146.18      
##  Mean   :168.3   Mean   :  33.32   Mean   : 4.506   Mean   :  713.77      
##  3rd Qu.:227.0   3rd Qu.:  23.00   3rd Qu.: 7.327   3rd Qu.:  527.30      
##  Max.   :723.0   Max.   :1600.00   Max.   :17.870   Max.   :18961.35      
##                                                                           
##   Hepatitis.B       Measles            BMI        under.five.deaths
##  Min.   : 2.00   Min.   :     0   Min.   : 2.00   Min.   :   0.0   
##  1st Qu.:74.00   1st Qu.:     0   1st Qu.:19.23   1st Qu.:   1.0   
##  Median :89.00   Median :    15   Median :43.80   Median :   4.0   
##  Mean   :79.16   Mean   :  2275   Mean   :38.15   Mean   :  45.3   
##  3rd Qu.:96.00   3rd Qu.:   368   3rd Qu.:55.80   3rd Qu.:  32.0   
##  Max.   :99.00   Max.   :131441   Max.   :77.10   Max.   :2100.0   
##                                                                    
##      Polio       Total.expenditure   Diphtheria       HIV.AIDS     
##  Min.   : 3.00   Min.   : 0.740    Min.   : 2.00   Min.   : 0.100  
##  1st Qu.:79.00   1st Qu.: 4.393    1st Qu.:81.00   1st Qu.: 0.100  
##  Median :92.00   Median : 5.860    Median :92.00   Median : 0.100  
##  Mean   :83.29   Mean   : 5.960    Mean   :84.06   Mean   : 2.028  
##  3rd Qu.:97.00   3rd Qu.: 7.480    3rd Qu.:97.00   3rd Qu.: 0.700  
##  Max.   :99.00   Max.   :14.390    Max.   :99.00   Max.   :50.600  
##                                                                    
##       GDP              Population        thinness..1.19.years
##  Min.   :     1.68   Min.   :3.400e+01   Min.   : 0.100      
##  1st Qu.:   462.24   1st Qu.:2.125e+05   1st Qu.: 1.600      
##  Median :  1620.57   Median :1.425e+06   Median : 3.000      
##  Mean   :  5642.83   Mean   :1.453e+07   Mean   : 4.827      
##  3rd Qu.:  4787.85   3rd Qu.:7.532e+06   3rd Qu.: 7.000      
##  Max.   :119172.74   Max.   :1.294e+09   Max.   :27.200      
##                                                              
##  thinness.5.9.years Income.composition.of.resources   Schooling    
##  Min.   : 0.100     Min.   :0.0000                  Min.   : 4.20  
##  1st Qu.: 1.600     1st Qu.:0.5060                  1st Qu.:10.30  
##  Median : 3.100     Median :0.6750                  Median :12.30  
##  Mean   : 4.886     Mean   :0.6314                  Mean   :12.12  
##  3rd Qu.: 7.100     3rd Qu.:0.7520                  3rd Qu.:14.00  
##  Max.   :28.200     Max.   :0.9360                  Max.   :20.70  
##                                                                    
##     latitude        longitude             ID           
##  Min.   :-38.42   Min.   :-175.198   Length:1602       
##  1st Qu.: -1.94   1st Qu.:  -8.224   Class :character  
##  Median : 15.45   Median :  23.881   Mode  :character  
##  Mean   : 16.43   Mean   :  15.585                     
##  3rd Qu.: 39.40   3rd Qu.:  46.869                     
##  Max.   : 60.13   Max.   : 179.414                     
## 

3.0.1.2 Graphical Summary

  • Histogram for Life Expectancy distribution: l

    • Left-skewed: the left tail extends towards 40-50 years, the right tail extends to 85-90 years.

    • Bimodal pattern: slightly dip before the highest peak –> One group with high life expectancy (70-80 years) and lower life expectancy (50-60 years)

ggplot(df, aes(x = Life.expectancy)) +
  geom_histogram(aes(y=after_stat(density)),binwidth = 2, fill = "#6A8CAF", color = "white", alpha = 0.8) +  
  geom_density(color = "blue", size = 0.5)+
  labs(title = "Distribution of Life Expectancy", x = "Life Expectancy (Years) ", y = "Count") +
  theme_minimal() +  
  theme(panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(), 
        panel.background = element_rect(fill = "white"),
        plot.title = element_text(hjust = 0.5))

  • Box plot of Life Expectancy between group of countries:

    • Developed countries: higher, less variability

    • Developing countries: lower, more variability

# Life expectancy between developed and developing country
ggplot(df, aes(x = Status, y = Life.expectancy, fill = Status)) +
  geom_boxplot(alpha = 0.7, outlier.shape = 16, outlier.size = 2, whiskerwidth = 1 ) + 
  scale_fill_manual(values = c("Developed" = "#AEC6CF", "Developing" = "#FFDAC1")) + 
  labs(title = "Life Expectancy in Developed vs. Developing Countries",
       x = "Status",
       y = "Life Expectancy",
       fill = "Country Status") +  # Legend title
  theme_minimal() +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_rect(fill = "white"),
        plot.title = element_text(hjust = 0.5, face = "bold", size = 9),
        legend.position = "none")

# Filter data for developing countries
df_developing <- df[df$Status == "Developing", ]

# Plot histogram
ggplot(df_developing, aes(x = Life.expectancy)) +
  geom_histogram(binwidth = 2, fill = "#FFDAC1", color = "black", alpha = 0.7) +
  labs(title = "Histogram of Life Expectancy in Developing Countries",
       x = "Life Expectancy (Years)",
       y = "Frequency") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"))

  • Outlier analysis:
developed_countries <- df[df$Status == "Developing"& df$Life.expectancy<50,]
id_list <- developed_countries$ID
library(ggplot2)
library(reshape2)
## 
## Attaching package: 'reshape2'
## The following object is masked from 'package:tidyr':
## 
##     smiths
# Columns to compare
columns_to_plot <- c(
  "percentage.expenditure", "Total.expenditure", 
  "GDP", "Income.composition.of.resources", 
  "Schooling", "Alcohol"
)

# Subset data for id_list and the rest of the data
developed_data <- df[df$ID %in% id_list, columns_to_plot]
other_data <- df[!df$ID %in% id_list, columns_to_plot]

# Create a new column to indicate the groups (id_list vs. rest of the data)
developed_data$Group <- "Outlier"
other_data$Group <- "Normal"

# Combine both datasets
combined_data <- rbind(developed_data, other_data)

# Reshape the data for plotting
combined_data_melted <- melt(combined_data, id.vars = "Group", 
                             variable.name = "Feature", value.name = "Value")

# Custom subplot names
feature_names <- c(
  "percentage.expenditure" = "Health Expenditure (%)",
  "Total.expenditure" = "Total Expenditure (%)",
  "GDP" = "GDP",
  "Income.composition.of.resources" = "Income Composition",
  "Schooling" = "Years of Schooling",
  "Alcohol" = "Alcohol Consumption"
)

# Create the box plots
ggplot(combined_data_melted, aes(x = Group, y = Value, fill = Group)) +
  geom_boxplot(outlier.size = 0.8, width = 0.3) +  # Smaller boxplots
  facet_wrap(~ Feature, scales = "free_y", ncol = 3, labeller = as_labeller(feature_names)) +  
  scale_fill_manual(values = c("Outlier" = "#FBB4AE", "Normal" = "#B3CDE3")) +  # Pastel colors
  labs(title = "Comparison of Selected Features between Outliers and Normal group",
       x="",
       y = "Value") +
  theme_minimal() +
  theme(panel.grid.major = element_blank(),
        panel.background = element_rect(fill = "white"),
        axis.text.x = element_text(size = 8),  # Smaller x-axis labels
        axis.text.y = element_text(size = 7),  # Smaller y-axis labels
        strip.text = element_text(size = 10),  # Smaller subplot titles
        plot.title = element_text(size = 14, hjust = 0.5, face = "bold"),
        panel.spacing = unit(1, "lines"))  

  • Correlation matrix: to have a first overview about linear relationship between variables
library(corrplot)
## corrplot 0.95 loaded
# Select only numeric columns (Base R)
numeric_cols <- sapply(df, is.numeric)  # Identify numeric columns
corr_matrix <- cor(df[, numeric_cols], use = "complete.obs")  # Compute correlation

# Plot the correlation matrix
corrplot(corr_matrix, tl.col = "darkblue", tl.srt = 45, method = "color")

# Add title
title("Correlation Matrix of Features", col.main = "black", font.main = 2, cex.main = 1.5)

3.0.2 Inference Analysis

3.0.2.1 Pearson Correlation Analysis

  • Goals: Quantify how strong these features related together in a linear relationship.

  • In this report, we chose 4 features which have a positive effect on Life Expectancy: GDP, Total Expenditure, Income Composition of Resources and Schooling.

    library(knitr)
    
    # Compute correlations
    correlations <- data.frame(
      Variable = c("GDP", "Total Expenditure", "Income Composition of Resources", "Schooling"),
      Pearson_Correlation = c(
        cor(df$Life.expectancy, df$GDP, use = "complete.obs", method = "pearson"),
        cor(df$Life.expectancy, df$Total.expenditure, use = "complete.obs", method = "pearson"),
        cor(df$Life.expectancy, df$Income.composition.of.resources, use = "complete.obs", method = "pearson"),
        cor(df$Life.expectancy, df$Schooling, use = "complete.obs", method = "pearson")
      )
    )
    
    # Display table
    kable(correlations, col.names = c("Variable", "Pearson Correlation"), caption = "Correlation with Life Expectancy")
    Correlation with Life Expectancy
    Variable Pearson Correlation
    GDP 0.4432124
    Total Expenditure 0.1810506
    Income Composition of Resources 0.7251987
    Schooling 0.7320096

The result shows that:

  • Moderate positive correlation between Life expectancy and GDP = higher GDP is generally associated with longer life expectancy.

  • Weak positive correlation between Life Expectancy and Total Expenditure = government health spending has a small effect. This might be because factors like efficiency of spending matter more.

  • Strong positive correlation between Life Expectancy and Income Composition of Resources = a country’s economic equality (human development factors like education, income distribution) has a significant effect on life expectancy.

3.0.2.2 Multiple linear regression model

  • Using p-value will help us in exclude variables that do not contribute significantly and focus on the most important predictors
df$Income.composition.norm <- scale(df$Income.composition.of.resources)
model <- lm(Life.expectancy ~ GDP + Total.expenditure + Income.composition.norm + Schooling, data = df)
summary(model)
## 
## Call:
## lm(formula = Life.expectancy ~ GDP + Total.expenditure + Income.composition.norm + 
##     Schooling, data = df)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -21.3218  -2.4649   0.6011   3.3994  17.7492 
## 
## Coefficients:
##                           Estimate Std. Error t value Pr(>|t|)    
## (Intercept)              5.379e+01  1.016e+00  52.965  < 2e-16 ***
## GDP                      7.018e-05  1.389e-05   5.054 4.83e-07 ***
## Total.expenditure       -1.290e-02  6.296e-02  -0.205    0.838    
## Income.composition.norm  3.326e+00  2.290e-01  14.527  < 2e-16 ***
## Schooling                1.253e+00  8.295e-02  15.101  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 5.626 on 1597 degrees of freedom
## Multiple R-squared:  0.6017, Adjusted R-squared:  0.6007 
## F-statistic: 603.1 on 4 and 1597 DF,  p-value: < 2.2e-16

The result show that:

  • Multiple R-squared = 0.6017: the model explains 60.17% of the variance in life expectancy

  • F-statistic = 603.1, p < 2.2e-16: the model is highly significant, meaning at least one predictor strongly affects life expectancy.

  • p-value: GDP and Income composition are significant effect in life expectancy (p-value <0.001). Especially Income composition with 1 unit increase in the variable results in a 3.3-year increase in life expectancy. On the other hand, Total expenditure has no significant effect on life expectancy at 5% level (p-value > 0.05)

Relationship between features

# Load necessary library
library(ggplot2)

# Define custom pastel colors
point_color <- "#56B4E9"  # Pastel blue for points
line_color <- "#E69F00"   # Pastel orange for regression line
fill_color <- "#E69F00"   # Light pastel orange for confidence interval

# GDP vs. Life Expectancy (Linear Regression)
p1 <- ggplot(df, aes(x = GDP, y = Life.expectancy)) +
  geom_point(alpha = 0.5, color = point_color) +  # Transparent pastel blue points
  geom_smooth(method = "lm", color = line_color, fill = fill_color, alpha = 0.3) +  # Regression line with shading
  labs(title = "GDP vs. Life Expectancy",
       x = "GDP per Capita",
       y = "Life Expectancy") +
  theme_minimal() +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_rect(fill = "white"),
        plot.title = element_text(hjust = 0.5, face = "bold", size = 13),
        legend.position = "none")

# Total Expenditure vs. Life Expectancy (Linear Regression)
p2 <- ggplot(df, aes(x = Total.expenditure, y = Life.expectancy)) +
  geom_point(alpha = 0.5, color = point_color) +
  geom_smooth(method = "lm", color = line_color, fill = fill_color, alpha = 0.3) +
  labs(title = "Healthcare Expenditure vs. Life Expectancy",
       x = "Total Healthcare Expenditure",
       y = "Life Expectancy") +
  theme_minimal() + 
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_rect(fill = "white"),
        plot.title = element_text(hjust = 0.5, face = "bold", size = 13),
        legend.position = "none")

# Income Composition of Resources vs. Life Expectancy (Linear Regression)
p3 <- ggplot(df, aes(x = Income.composition.norm, y = Life.expectancy)) +
  geom_point(alpha = 0.5, color = point_color) +
  geom_smooth(method = "lm", color = line_color, fill = fill_color, alpha = 0.3) +
  labs(title = "Income Composition vs. Life Expectancy",
       x = "Income Composition of Resources",
       y = "Life Expectancy") +
  theme_minimal() +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_rect(fill = "white"),
        plot.title = element_text(hjust = 0.5, face = "bold", size = 13),
        legend.position = "none")
# Schooling vs. Life Expectancy (Linear Regression)
p4 <- ggplot(df, aes(x = Schooling, y = Life.expectancy)) +
  geom_point(alpha = 0.5, color = point_color) +
  geom_smooth(method = "lm", color = line_color, fill = fill_color, alpha = 0.3) +
  labs(title = "Schooling vs. Life Expectancy",
       x = "Schooling",
       y = "Life Expectancy") +
  theme_minimal() +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_rect(fill = "white"),
        plot.title = element_text(hjust = 0.5, face = "bold", size = 13),
        legend.position = "none")

# Display plots together
library(gridExtra)
## 
## Attaching package: 'gridExtra'
## The following object is masked from 'package:dplyr':
## 
##     combine
grid.arrange(p1, p2, p3,p4, ncol = 1)  # Arrange plots in a single row
## `geom_smooth()` using formula = 'y ~ x'
## `geom_smooth()` using formula = 'y ~ x'
## `geom_smooth()` using formula = 'y ~ x'
## `geom_smooth()` using formula = 'y ~ x'

3.0.3 Perform Spatial Clustering with Moran’s I Test

  • Moran’s I statistic measures how similar neighboring observations are.

  • Result interpretation:

    • p-value < 0.001Statistically significant, meaning life expectancy is not randomly distributed but follows a spatial pattern

    • Moran’s I = 0.921 → Strong positive spatial autocorrelation (high/low life expectancy regions cluster together).

library(leaflet)
library(sf)
library(rnaturalearth)
library(rnaturalearthdata)
## 
## Attaching package: 'rnaturalearthdata'
## The following object is masked from 'package:rnaturalearth':
## 
##     countries110
library(spdep)  # For spatial clustering
library(dplyr)
# Convert dataframe to an sf spatial object
df_sf <- st_as_sf(df, coords = c("longitude", "latitude"), crs = 4326)

# Slightly jitter coordinates to avoid identical points issue
set.seed(42)
df_sf$geometry <- st_jitter(df_sf$geometry, amount = 1e-5)

# Load world map data
world <- ne_countries(scale = "medium", returnclass = "sf")  # Keep separate!

# Define color palette based on Life Expectancy
pal <- colorNumeric(palette = "viridis", domain = df_sf$Life.expectancy)

# Create spatial neighbors using k-nearest neighbors
coords <- st_coordinates(df_sf)
neighbors <- knearneigh(coords, k = 5)
nb <- knn2nb(neighbors)

# Convert to spatial weights
weights <- nb2listw(nb, style = "W")

# Compute Moran's I test for spatial clustering
morans_test <- moran.test(df_sf$Life.expectancy, listw = weights)

print(morans_test)  # Check p-value to confirm clustering
## 
##  Moran I test under randomisation
## 
## data:  df_sf$Life.expectancy  
## weights: weights    
## 
## Moran I statistic standard deviate = 62.203, p-value < 2.2e-16
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      0.9210103270     -0.0006246096      0.0002195275
  • Interactive Spatial Mapping: we can see clearly the cluster of country with high and low life expectancy
# Create an interactive leaflet map
leaflet() %>%
  addProviderTiles(providers$CartoDB.Positron) %>%  # Add basemap

  # Add world map as a background layer (optional)
  addPolygons(data = world, fillOpacity = 0.1, color = "gray", weight = 1) %>%

  # Add life expectancy points
  addCircleMarkers(
    data = df_sf,
    radius = 5,
    color = ~pal(Life.expectancy),
    stroke = FALSE,
    fillOpacity = 0.8,
    popup = ~paste0("Country: ", Country, "<br>",
                    "Life Expectancy: ", round(Life.expectancy, 2))
  ) %>%

  # Add legend
  addLegend(
    "bottomright",
    pal = pal,
    values = df_sf$Life.expectancy,
    title = "Life Expectancy",
    opacity = 1
  ) %>%
  addScaleBar(position = "bottomleft")

4 Machine Learning for Spatial Prediction

  • Goals: to predict high-risk regions (regions where life expectancy is in the bottom 25%) based on geographical and socioeconomic information. This is framed as a binary classification problem, where:

    • 1 = high-risk region (low life expectancy).

    • 0 = low-risk region (higher life expectancy).

  • Methodology: Using Random Forest model which can handles non-linear relationships data. Since life expectancy can have non-linear dependencies on other features.

  • Evaluation: using ROC curve

library(caret)           # Machine learning utilities
## Loading required package: lattice
## 
## Attaching package: 'caret'
## The following object is masked from 'package:purrr':
## 
##     lift
library(randomForest)    # Random Forest model
## randomForest 4.7-1.2
## Type rfNews() to see new features/changes/bug fixes.
## 
## Attaching package: 'randomForest'
## The following object is masked from 'package:gridExtra':
## 
##     combine
## The following object is masked from 'package:dplyr':
## 
##     combine
## The following object is masked from 'package:ggplot2':
## 
##     margin
library(ggplot2)         # Visualization
library(sf)              # Spatial data handling
library(dplyr)           # Data manipulation

# Define High-Risk Regions (Bottom 25% Life Expectancy)
threshold <- quantile(df$Life.expectancy, 0.25, na.rm = TRUE) 
df$HighRisk <- ifelse(df$Life.expectancy < threshold, 1, 0) 

# Ensure HighRisk is a Factor
df$HighRisk <- factor(df$HighRisk, levels = c(0, 1))

# Select Features for Prediction
df_ml <- subset(df, select = c(HighRisk, GDP, percentage.expenditure, Population, Schooling, Income.composition.of.resources, latitude, longitude))


# Train-Test Split (80% Train, 20% Test)
set.seed(42)
trainIndex <- createDataPartition(df_ml$HighRisk, p = 0.8, list = FALSE)
trainData <- df_ml[trainIndex, ]
testData <- df_ml[-trainIndex, ]

# Train Random Forest Model
rf_model <- randomForest(HighRisk ~ ., data = trainData, ntree = 500, mtry = 3, importance = TRUE)

# Model Evaluation on Test Data
predictions <- predict(rf_model, testData)

# Ensure predictions and test labels have same factor levels
testData$HighRisk <- factor(testData$HighRisk, levels = c(0, 1))
predictions <- factor(predictions, levels = c(0, 1))

# Compute Confusion Matrix
conf_matrix <- confusionMatrix(predictions, testData$HighRisk)
print(conf_matrix)  # Accuracy, Precision, Recall
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0 235   5
##          1   5  74
##                                           
##                Accuracy : 0.9687          
##                  95% CI : (0.9431, 0.9849)
##     No Information Rate : 0.7524          
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.9159          
##                                           
##  Mcnemar's Test P-Value : 1               
##                                           
##             Sensitivity : 0.9792          
##             Specificity : 0.9367          
##          Pos Pred Value : 0.9792          
##          Neg Pred Value : 0.9367          
##              Prevalence : 0.7524          
##          Detection Rate : 0.7367          
##    Detection Prevalence : 0.7524          
##       Balanced Accuracy : 0.9579          
##                                           
##        'Positive' Class : 0               
## 
# Feature Importance
importance(rf_model)
##                                         0        1 MeanDecreaseAccuracy
## GDP                             12.383482 11.84097             18.84949
## percentage.expenditure           7.766477 10.45633             13.64698
## Population                      12.100128 10.57706             16.01370
## Schooling                       21.332472 26.37147             34.55436
## Income.composition.of.resources 30.667872 65.25777             61.73929
## latitude                        31.625872 39.81425             47.70672
## longitude                       29.957300 35.45906             39.17277
##                                 MeanDecreaseGini
## GDP                                     30.56907
## percentage.expenditure                  18.14733
## Population                              18.67553
## Schooling                               89.64216
## Income.composition.of.resources        212.40755
## latitude                                54.36241
## longitude                               56.20256
varImpPlot(rf_model)

# Check if both classes are predicted
table(predictions)
## predictions
##   0   1 
## 240  79
table(testData$HighRisk)
## 
##   0   1 
## 240  79
# Visualization: Map of Predicted High-Risk Regions
df_sf <- st_as_sf(df, coords = c("longitude", "latitude"), crs = 4326) # Convert to Spatial Object
df_sf$Predicted_Risk <- predict(rf_model, df_ml)
cm <- conf_matrix$table
# True Positives (TP), False Negatives (FN), False Positives (FP), and True Negatives (TN)
TP <- cm[2, 2]  # High-Risk correctly predicted as High-Risk
FN <- cm[1, 2]  # Non-High-Risk incorrectly predicted as High-Risk
FP <- cm[2, 1]  # High-Risk incorrectly predicted as Non-High-Risk
TN <- cm[1, 1]  # Non-High-Risk correctly predicted as Non-High-Risk

# True Positive Rate (TPR) and False Positive Rate (FPR)
TPR <- TP / (TP + FN)  # True Positive Rate
FPR <- FP / (FP + TN)  # False Positive Rate

# Print TPR and FPR
cat("True Positive Rate (TPR):", TPR, "\n")
## True Positive Rate (TPR): 0.9367089
cat("False Positive Rate (FPR):", FPR, "\n")
## False Positive Rate (FPR): 0.02083333
ggplot() +
  geom_sf(data = df_sf, aes(color = as.factor(Predicted_Risk))) +
  scale_color_manual(values = c("#B3CDE3", "#FFB3BA"), labels = c("Low-Risk","High-Risk")) +
  labs(title = "Predicted High-Risk Regions", color = "Risk Level") +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 16, face = "bold"), 

  )

library(pROC)  
## Type 'citation("pROC")' for a citation.
## 
## Attaching package: 'pROC'
## The following objects are masked from 'package:stats':
## 
##     cov, smooth, var
pred_probs <- predict(rf_model, testData, type = "prob")[,2]  # Probabilities for High-Risk class (1)

# Compute ROC Curve
roc_obj <- roc(testData$HighRisk, pred_probs)
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
# Plot ROC Curve
ggplot(data = data.frame(TPR = roc_obj$sensitivities, 
                         FPR = 1 - roc_obj$specificities)) +
  geom_line(aes(x = FPR, y = TPR), color = "#CDB7F6", size = 1) +
  geom_abline(slope = 1, intercept = 0, linetype = "dashed", color = "grey") +
  labs(title = paste("ROC Curve"),
       x = "False Positive Rate ",
       y = "True Positive Rate ") +
  theme_minimal()

Result interpretation:

  • The ROC curve stay away from the random line model –> good classification model.

5 Conclusion

  • Having an overview about Life Expectancy between various region and which factor can impact it, using multiples Exploratory Data Analysis Method and Spatial Analysis Method.

  • Adapting a machine learning model to building a classifier for high-risk region, using geographical and socioeconomic features. These model can be helpful in determining strategy for improving Life expectancy within countries.

LS0tCnRpdGxlOiAiVW5kZXJzdGFuZGluZyBMaWZlIEV4cGVjdGFuY3kgVGhyb3VnaCBTcGF0aWFsIERhdGEgQW5hbHlzaXMgIgphdXRob3I6ICJOZ3V5ZW4gVGhpIFZhbiAtIDIyQkkxMzQ1OSIKZGF0ZTogIjE3LTAzLTIwMjUiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogVFJVRQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBudW1iZXJfc2VjdGlvbnM6IFRSVUUKICAgIHRoZW1lOiBmbGF0bHkKICAgIHRvYzogVFJVRQogICAgdG9jX2Zsb2F0OiBUUlVFCiAgICBkZXY6ICJzdmciCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgojIEludHJvZHVjdGlvbgoKLSAgIE9iamVjdGl2ZXM6IHVzaW5nIHNwYXRpYWwgZGF0YSBhbmFseXNpcyBtZXRob2RzIHRvIHN0dWR5IHRoZSBMaWZlIEV4cGVjdGFuY3kgYWNyb3NzIHZhcmlvdXMgY291bnRyaWVzLCBhaW1pbmcgdG8gdW5jb3ZlciBwYXR0ZXJucyBhbmQgZGlzcGFyaXRpZXMgYmV0d2VlbiBncm91cHMgb2YgY291bnRyaWVzLgoKLSAgIFBvdGVudGlhbCBhbmFseXNpcyBtZXRob2RzOiBVc2luZyBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIChEZXNjcmlwdGl2ZSBhbmQgSW5mZXJlbmNlIEFuYWx5c2lzKSBhbmQgTWFjaGluZSBMZWFybmluZyBmb3IgU3BhdGlhbCBQcmVkaWN0aW9uLgoKIyBEYXRhIERlc2NyaXB0aW9uCgotICAgVGhpcyBkYXRhc2V0IGV4YW1pbmVzIHRoZSBnZW9ncmFwaGljIGFuZCBzb2Npb2Vjb25vbWljIGZhY3RvcnMgaW5mbHVlbmNpbmcgbGlmZSBleC0gcGVjdGFuY3kgYWNyb3NzIHZhcmlvdXMgY291bnRyaWVzIGFuZCB5ZWFycyAoZnJvbSAyMDAwIHRvIDIwMTUpLgoKLSAgIFNvdXJjZTogS2FnZ2xlKCBbbGlua10oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cy9zYXVyYWJoYmFkb2xlL2xpZmUtZXhwZWN0YW5jeS1iYXNlZC1vbi1nZW9ncmFwaGljLWxvY2F0aW9ucz9zZWxlY3Q9TGlmZUV4cGVjdGFuY3kuY3N2KSApCgotICAgRm9yIHNwYXRpYWwgYXR0cmlidXRlIChsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlKSwgdGhlc2UgdmFsdWUgYXJlIG1lcmdlIGZyb20gYSBjb3VudHJpZXMgY29vcmRpbmF0ZXMgZGF0YXNldCAoW2xpbmtdKGh0dHBzOi8vZ2lzdC5naXRodWIuY29tL21ldGFsM2QvNWI5MjUwNzdlNjYxOTQ1NTFkZjk0OWRlNjRlOTEwZjYpKSBiYXNlZCBvbiBDb3VudHJ5IG5hbWUKCi0gICBGZWF0dXJlIGRlc2NyaXB0aW9uczoKCiAgICBgYGB7ciB3YXJuaW5nPUZBTFNFfQogICAgICAgIGxpYnJhcnkoa25pdHIpCiAgICBsaWJyYXJ5KGthYmxlRXh0cmEpCgogICAgIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGgga2V5IGF0dHJpYnV0ZXMKICAgIGtleV9hdHRyaWJ1dGVzIDwtIGRhdGEuZnJhbWUoCiAgICAgIEZlYXR1cmUgPSBjKCJDb3VudHJ5IiwgIlllYXIiLCAiU3RhdHVzIiwgIkxvbmdpdHVkZSIsICJMYXRpdHVkZSIsIAogICAgICAgICAgICAgICAgICAiTGlmZSBleHBlY3RhbmN5IiwgIkFkdWx0IE1vcnRhbGl0eSIsICJJbmZhbnQgZGVhdGhzIiwgCiAgICAgICAgICAgICAgICAgICJBbGNvaG9sIiwgIlBlcmNlbnRhZ2UgZXhwZW5kaXR1cmUiLCAiSGVwYXRpdGlzIEIiLCAKICAgICAgICAgICAgICAgICAgIk1lYXNsZXMiLCAiQk1JIiwgIlVuZGVyLWZpdmUgZGVhdGhzIiwgIlBvbGlvIiwgCiAgICAgICAgICAgICAgICAgICJUb3RhbCBleHBlbmRpdHVyZSIsICJEaXBodGhlcmlhIiwgIkhJVi9BSURTIiwgIkdEUCIsIAogICAgICAgICAgICAgICAgICAiUG9wdWxhdGlvbiIsICJUaGlubmVzcyAxLTE5IHllYXJzIiwgIlRoaW5uZXNzIDUtOSB5ZWFycyIsIAogICAgICAgICAgICAgICAgICAiSW5jb21lIGNvbXBvc2l0aW9uIG9mIHJlc291cmNlcyIsICJTY2hvb2xpbmciKSwKICAgICAgRGVzY3JpcHRpb24gPSBjKAogICAgICAgICJOYW1lIG9mIHRoZSBjb3VudHJ5IiwKICAgICAgICAiWWVhciBvZiBvYnNlcnZhdGlvbiIsCiAgICAgICAgIlVyYmFuIG9yIHJ1cmFsIHN0YXR1cyIsCiAgICAgICAgIkxvbmdpdHVkZSBmb3IgZWFjaCBjb3VudHJ5IiwKICAgICAgICAiTGF0aXR1ZGUgZm9yIGVhY2ggY291bnRyeSIsCiAgICAgICAgIkxpZmUgZXhwZWN0YW5jeSBhdCBiaXJ0aCBpbiB5ZWFycyAoYXZlcmFnZSBwZXJpb2QgdGhhdCBhIHBlcnNvbiBtYXkgZXhwZWN0IHRvIGxpdmUpIiwKICAgICAgICAiUHJvYmFiaWxpdHkgb2YgZHlpbmcgYmV0d2VlbiAxNSBhbmQgNjAgeWVhcnMgcGVyIDEwMDAiLAogICAgICAgICJOdW1iZXIgb2YgaW5mYW50IGRlYXRocyBwZXIgMTAwMCBwb3B1bGF0aW9uIiwKICAgICAgICAiQWxjb2hvbCBjb25zdW1wdGlvbiwgbWVhc3VyZWQgYXMgbGl0ZXJzIHBlciBjYXBpdGEiLAogICAgICAgICJFeHBlbmRpdHVyZSBvbiBoZWFsdGggYXMgYSBwZXJjZW50YWdlIG9mIEdEUCIsCiAgICAgICAgIkhlcGF0aXRpcyBCIGltbXVuaXphdGlvbiBjb3ZlcmFnZSBhbW9uZyAxLXllYXItb2xkcyAoJSkiLAogICAgICAgICJOdW1iZXIgb2YgcmVwb3J0ZWQgbWVhc2xlcyBjYXNlcyBwZXIgMTAwMCBwb3B1bGF0aW9uIiwKICAgICAgICAiQXZlcmFnZSBCb2R5IE1hc3MgSW5kZXggb2YgdGhlIHBvcHVsYXRpb24iLAogICAgICAgICJOdW1iZXIgb2YgZGVhdGhzIHVuZGVyIGFnZSBmaXZlIHBlciAxMDAwIHBvcHVsYXRpb24iLAogICAgICAgICJQb2xpbyBpbW11bml6YXRpb24gY292ZXJhZ2UgYW1vbmcgMS15ZWFyLW9sZHMgKCUpIiwKICAgICAgICAiVG90YWwgZ292ZXJubWVudCBoZWFsdGggZXhwZW5kaXR1cmUgYXMgYSBwZXJjZW50YWdlIG9mIEdEUCIsCiAgICAgICAgIkRpcGh0aGVyaWEgdGV0YW51cyB0b3hvaWQgYW5kIHBlcnR1c3NpcyBpbW11bml6YXRpb24gY292ZXJhZ2UgYW1vbmcgMS15ZWFyLW9sZHMgKCUpIiwKICAgICAgICAiRGVhdGhzIHBlciAxLDAwMCBsaXZlIGJpcnRocyBkdWUgdG8gSElWL0FJRFMgKDAtNCB5ZWFycykiLAogICAgICAgICJHcm9zcyBEb21lc3RpYyBQcm9kdWN0IHBlciBjYXBpdGEgKGluIFVTRCkiLAogICAgICAgICJQb3B1bGF0aW9uIG9mIHRoZSBjb3VudHJ5IiwKICAgICAgICAiUHJldmFsZW5jZSBvZiB0aGlubmVzcyBhbW9uZyBjaGlsZHJlbiBhbmQgYWRvbGVzY2VudHMgYWdlZCAxMC0xOSAoJSkiLAogICAgICAgICJQcmV2YWxlbmNlIG9mIHRoaW5uZXNzIGFtb25nIGNoaWxkcmVuIGFnZWQgNS05ICglKSIsCiAgICAgICAgIkh1bWFuIERldmVsb3BtZW50IEluZGV4IGluIHRlcm1zIG9mIGluY29tZSBjb21wb3NpdGlvbiBvZiByZXNvdXJjZXMgKDAgdG8gMSkiLAogICAgICAgICJOdW1iZXIgb2YgeWVhcnMgb2Ygc2Nob29saW5nIgogICAgICApCiAgICApCgogICAgIyBEaXNwbGF5IHRhYmxlCiAgICBrYWJsZShrZXlfYXR0cmlidXRlcywgY29sLm5hbWVzID0gYygiRmVhdHVyZSIsICJEZXNjcmlwdGlvbiIpLCBjYXB0aW9uID0gIktleSBGZWF0dXJlcyIpICU+JQogICAgICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJib3JkZXJlZCIpLCBmdWxsX3dpZHRoID0gRkFMU0UpICU+JQogICAgICBjb2x1bW5fc3BlYygxLCBib2xkID0gVFJVRSwgYm9yZGVyX3JpZ2h0ID0gVFJVRSkgJT4lCiAgICAgIGNvbHVtbl9zcGVjKDIsIHdpZHRoID0gIjYwJSIpICU+JQogICAgICByb3dfc3BlYygwLCBib2xkID0gVFJVRSwgYmFja2dyb3VuZCA9ICIjZjdmN2Y3IikKCgoKICAgIGBgYAoKTG9hZCBMaWJyYXJpZXMKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHNmKQpsaWJyYXJ5KHNwZGVwKQpsaWJyYXJ5KEdXbW9kZWwpCmxpYnJhcnkodG1hcCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQpgYGAKCiMjIERhdGEgUHJlcHJvY2Vzc2luZwoKLSAgIENoZWNraW5nIGZvciBtaXNzaW5nIHZhbHVlIHdpdGhpbiBmZWF0dXJlczoKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CiMgUmVhZCBkYXRhc2V0cwpkZiA8LSByZWFkLmNzdigiTGlmZUV4cGVjdGFuY3kuY3N2IikKY29vcmRpbmF0ZXMgPC0gcmVhZC5jc3YoImNvb3JkaW5hdGUuY3N2IikKCiMgTWVyZ2UgZGF0YXNldHMgYmFzZWQgb24gY291bnRyeSBuYW1lCmRmIDwtIG1lcmdlKGRmLCBjb29yZGluYXRlc1ssIGMoIm5hbWUiLCAibGF0aXR1ZGUiLCAibG9uZ2l0dWRlIildLCAKICAgICAgICAgICAgYnkueCA9ICJDb3VudHJ5IiwgYnkueSA9ICJuYW1lIiwgYWxsLnggPSBUUlVFKQpkZiRJRCA8LSBwYXN0ZShkZiRDb3VudHJ5LCBkZiRZZWFyLCBzZXAgPSAiXyIpCgojIENvbXB1dGUgbWlzc2luZyB2YWx1ZSBjb3VudHMgYW5kIHBlcmNlbnRhZ2VzCm5hX2NvdW50cyA8LSBjb2xTdW1zKGlzLm5hKGRmWywgIW5hbWVzKGRmKSAlaW4lIGMoIklEIildKSkKdG90YWxfY291bnRzIDwtIG5yb3coZGYpCm5hX3BlcmNlbnRhZ2UgPC0gKG5hX2NvdW50cyAvIHRvdGFsX2NvdW50cykgKiAxMDAgICMgQ29udmVydCB0byBwZXJjZW50YWdlCgojIENvbnZlcnQgdG8gZGF0YSBmcmFtZSBmb3IgdmlzdWFsaXphdGlvbgpuYV9kZiA8LSBkYXRhLmZyYW1lKEZlYXR1cmUgPSBuYW1lcyhuYV9jb3VudHMpLCBOQV9Db3VudCA9IG5hX2NvdW50cywgTkFfUGVyY2VudGFnZSA9IG5hX3BlcmNlbnRhZ2UpCnBhc3RlbF9jb2xvcnMgPC0gYygiI0ZCQjRBRSIsICIjQjNDREUzIiwgIiNDQ0VCQzUiLCAiI0RFQ0JFNCIsICIjRkVEOUE2IiwgIiNGRkZGQ0MiLCAKICAgICAgICAgICAgICAgICAgICIjRTVEOEJEIiwgIiNGRERBRUMiLCAiI0YyRjJGMiIsICIjQTZEODU0IiwgIiM2NkMyQTUiLCAiI0ZDOEQ2MiIsIAogICAgICAgICAgICAgICAgICAgIiM4REEwQ0IiLCAiI0U3OEFDMyIsICIjRkZEOTJGIiwgIiNBNkNFRTMiLCAiI0IyREY4QSIsICIjRkI5QTk5IiwgCiAgICAgICAgICAgICAgICAgICAiI0NBQjJENiIsICIjRkZFRDZGIiwgIiNCQzgwQkQiLCAiIzgwQjFEMyIsICIjRkZCM0IzIiwgIiNCRUJBREEiKQojIFBsb3QgbWlzc2luZyB2YWx1ZXMgYXMgYSBob3Jpem9udGFsIGJhciBjaGFydCB3aXRoIHBlcmNlbnRhZ2UKZ2dwbG90KG5hX2RmLCBhZXMoeCA9IE5BX1BlcmNlbnRhZ2UsIHkgPSByZW9yZGVyKEZlYXR1cmUsIE5BX1BlcmNlbnRhZ2UpLCBmaWxsID0gRmVhdHVyZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBsYWJzKHRpdGxlID0gIk1pc3NpbmcgVmFsdWVzIG9mIEZlYXR1cmVzICIsCiAgICAgICB4ID0gIlBlcmNlbnRhZ2Ugb2YgTWlzc2luZyBWYWx1ZXMgKCUpIiwKICAgICAgIHkgPSAiRmVhdHVyZSIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSA1KSkgKyAgIyBBZGQgeC1heGlzIHRpY2tzIGV2ZXJ5IDUlCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFzdGVsX2NvbG9ycykgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICAjIEFkanVzdCB0ZXh0IHNpemUgZm9yIHJlYWRhYmlsaXR5CgpgYGAKCi0gICBEYXRhIENsZWFuaW5nOiB0byBhdm9pZCBlcnJvciB3aXRoIG1pc3NpbmcgZGF0YSwgd2UgY2FuIHJlbW92ZSB0aGVtIGZyb20gdGhlIGRhdGFzZXQuCgpEYXRhIHNpemUgYmVmb3JlIGNsZWFuaW5nOgoKYGBge3Igd2FybmluZz1GQUxTRX0KcHJpbnQoZGltKGRmKSkKCgpgYGAKCkRhdGEgc2l6ZSBhZnRlciBjbGVhbmluZzoKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CmRmIDwtIGRmICU+JSBkcm9wX25hKCkgICMgUmVtb3ZlIE5BIHZhbHVlcwpkZiRDb3VudHJ5IDwtIGFzLmZhY3RvcihkZiRDb3VudHJ5KSAjIENvbnZlcnQgY2F0ZWdvcmljYWwgdmFyaWFibGVzCmRmJFN0YXR1cyA8LSBhcy5mYWN0b3IoZGYkU3RhdHVzKSAKcHJpbnQoZGltKGRmKSkKYGBgCgojIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMKCi0gICBJbiB0aGlzIHBhcnQsIHdlIGZvY3VzIG9uIHRoZSBrZXkgZmVhdHVyZTogTGlmZSBFeHBlY3RhbmN5LgoKLSAgIE1ldGhvZG9sb2d5OiBkb2luZyBkZXNjcmlwdGl2ZSBhbmFseXNpcyB0aGVuIGluZmVyZW5jZSBhbmFseXNpcyB0byBoYXZlIGFuIG92ZXJ2aWV3IHVuZGVyc3RhbmRpbmcgYWJvdXQgdGhlIGdlbmVyYWwgdHJlbmQgb2YgdGhpcyBmZWF0dXJlIOKAk1w+IENvbmNsdXNpb246IHdoaWNoIG90aGVyIGZlYXR1cmVzIGNhbiBoYXZlIGFuIGVmZmVjdCBvbiBMaWZlIEV4cGVjdGFuY3kuCgojIyMgRGVzY3JpcHRpdmUgQW5hbHlzaXMKCiMjIyMgTnVtZXJpY2FsIFN1bW1hcnkgTWVhc3VyZXMKCi0gICBVc2luZyBzdW1tYXJ5IHN0YXRpc3RpY3MgdG8gdW5kZXJzdGFuZCB0aGUgZ2VuZXJhbCB0cmVuZHMgYW5kIGdpdmluZyBxdWFudGl0YXRpdmUgaW5zaWdodC4KCiAgICBgYGB7ciB3YXJuaW5nPUZBTFNFfQogICAgIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMKICAgIGxpYnJhcnkoZHBseXIpCiAgICBsaWJyYXJ5KGtuaXRyKQogICAgbGlicmFyeShrYWJsZUV4dHJhKSAgIyBGb3IgZW5oYW5jZWQgdGFibGUgZm9ybWF0dGluZwoKICAgICMgQ29tcHV0ZSBzdW1tYXJ5IHN0YXRpc3RpY3MgZm9yIExpZmUgRXhwZWN0YW5jeQogICAgc3VtbWFyeV90YWJsZSA8LSBkZiAlPiUKICAgICAgZ3JvdXBfYnkoU3RhdHVzKSAlPiUKICAgICAgc3VtbWFyaXNlKAogICAgICAgIENvdW50ID0gbigpLAogICAgICAgIE1lYW4gPSByb3VuZChtZWFuKExpZmUuZXhwZWN0YW5jeSwgbmEucm0gPSBUUlVFKSwgMyksCiAgICAgICAgU3RhbmRhcmRfRGV2aWF0aW9uID0gcm91bmQoc2QoTGlmZS5leHBlY3RhbmN5LCBuYS5ybSA9IFRSVUUpLCAzKSwKICAgICAgICBNaW4gPSBtaW4oTGlmZS5leHBlY3RhbmN5LCBuYS5ybSA9IFRSVUUpLAogICAgICAgIGBQZXJjZW50aWxlIDI1JWAgPSByb3VuZChxdWFudGlsZShMaWZlLmV4cGVjdGFuY3ksIDAuMjUsIG5hLnJtID0gVFJVRSksIDMpLAogICAgICAgIGBQZXJjZW50aWxlIDUwJWAgPSByb3VuZChtZWRpYW4oTGlmZS5leHBlY3RhbmN5LCBuYS5ybSA9IFRSVUUpLCAzKSwKICAgICAgICBgUGVyY2VudGlsZSA3NSVgID0gcm91bmQocXVhbnRpbGUoTGlmZS5leHBlY3RhbmN5LCAwLjc1LCBuYS5ybSA9IFRSVUUpLCAzKSwKICAgICAgICBNYXggPSBtYXgoTGlmZS5leHBlY3RhbmN5LCBuYS5ybSA9IFRSVUUpCiAgICAgICkgJT4lCiAgICAgIGJpbmRfcm93cygKICAgICAgICBkZiAlPiUgc3VtbWFyaXNlKAogICAgICAgICAgU3RhdHVzID0gIkFsbCBDb3VudHJpZXMiLAogICAgICAgICAgQ291bnQgPSBuKCksCiAgICAgICAgICBNZWFuID0gcm91bmQobWVhbihMaWZlLmV4cGVjdGFuY3ksIG5hLnJtID0gVFJVRSksIDMpLAogICAgICAgICAgU3RhbmRhcmRfRGV2aWF0aW9uID0gcm91bmQoc2QoTGlmZS5leHBlY3RhbmN5LCBuYS5ybSA9IFRSVUUpLCAzKSwKICAgICAgICAgIE1pbiA9IG1pbihMaWZlLmV4cGVjdGFuY3ksIG5hLnJtID0gVFJVRSksCiAgICAgICAgICBgUGVyY2VudGlsZSAyNSVgID0gcm91bmQocXVhbnRpbGUoTGlmZS5leHBlY3RhbmN5LCAwLjI1LCBuYS5ybSA9IFRSVUUpLCAzKSwKICAgICAgICAgIGBQZXJjZW50aWxlIDUwJWAgPSByb3VuZChtZWRpYW4oTGlmZS5leHBlY3RhbmN5LCBuYS5ybSA9IFRSVUUpLCAzKSwKICAgICAgICAgIGBQZXJjZW50aWxlIDc1JWAgPSByb3VuZChxdWFudGlsZShMaWZlLmV4cGVjdGFuY3ksIDAuNzUsIG5hLnJtID0gVFJVRSksIDMpLAogICAgICAgICAgTWF4ID0gbWF4KExpZmUuZXhwZWN0YW5jeSwgbmEucm0gPSBUUlVFKQogICAgICAgICkKICAgICAgKQoKICAgICMgVHJhbnNwb3NlIHRoZSB0YWJsZQogICAgc3VtbWFyeV90YWJsZV90cmFuc3Bvc2VkIDwtIGFzLmRhdGEuZnJhbWUodChzdW1tYXJ5X3RhYmxlKSkKCiAgICAjIFJlbmFtZSBjb2x1bW5zIHdpdGggY291bnRyeSBzdGF0dXMKICAgIGNvbG5hbWVzKHN1bW1hcnlfdGFibGVfdHJhbnNwb3NlZCkgPC0gc3VtbWFyeV90YWJsZV90cmFuc3Bvc2VkWzEsIF0gICMgRmlyc3Qgcm93IGFzIGNvbHVtbiBuYW1lcwogICAgc3VtbWFyeV90YWJsZV90cmFuc3Bvc2VkIDwtIHN1bW1hcnlfdGFibGVfdHJhbnNwb3NlZFstMSwgXSAgIyBSZW1vdmUgdGhlIGZpcnN0IHJvdwoKICAgICMgUHJpbnQgdHJhbnNwb3NlZCBzdW1tYXJ5IHRhYmxlIHdpdGggYmV0dGVyIHN0eWxpbmcKICAgIGthYmxlKHN1bW1hcnlfdGFibGVfdHJhbnNwb3NlZCwgY2FwdGlvbiA9ICJTdW1tYXJ5IFN0YXRpc3RpY3Mgb2YgTGlmZSBFeHBlY3RhbmN5IikgJT4lCiAgICAgIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIsICJyZXNwb25zaXZlIiksIGZ1bGxfd2lkdGggPSBGQUxTRSkgJT4lCiAgICAgIGNvbHVtbl9zcGVjKDEsIGJvcmRlcl9yaWdodCA9IFRSVUUsIGJvbGQgPSBUUlVFKSAlPiUgICMgQWRkIHZlcnRpY2FsIGJvcmRlciBhbmQgYm9sZCBmaXJzdCBjb2x1bW4KICAgICAgcm93X3NwZWMoMCwgYm9sZCA9IFRSVUUpICAjIEJvbGQgaGVhZGVyIHJvdwoKCgogICAgYGBgCgotICAgRm9yIG90aGVyIGZlYXR1cmVzOgoKYGBge3Igd2FybmluZz1GQUxTRX0Kc3VtbWFyeShkZikKYGBgCgojIyMjIEdyYXBoaWNhbCBTdW1tYXJ5CgotICAgSGlzdG9ncmFtIGZvciBMaWZlIEV4cGVjdGFuY3kgZGlzdHJpYnV0aW9uOiBsCgogICAgLSAgIExlZnQtc2tld2VkOiB0aGUgbGVmdCB0YWlsIGV4dGVuZHMgdG93YXJkcyA0MC01MCB5ZWFycywgdGhlIHJpZ2h0IHRhaWwgZXh0ZW5kcyB0byA4NS05MCB5ZWFycy4KCiAgICAtICAgQmltb2RhbCBwYXR0ZXJuOiBzbGlnaHRseSBkaXAgYmVmb3JlIHRoZSBoaWdoZXN0IHBlYWsg4oCTXD4gT25lIGdyb3VwIHdpdGggaGlnaCBsaWZlIGV4cGVjdGFuY3kgKDcwLTgwIHllYXJzKSBhbmQgbG93ZXIgbGlmZSBleHBlY3RhbmN5ICg1MC02MCB5ZWFycykKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CmdncGxvdChkZiwgYWVzKHggPSBMaWZlLmV4cGVjdGFuY3kpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHk9YWZ0ZXJfc3RhdChkZW5zaXR5KSksYmlud2lkdGggPSAyLCBmaWxsID0gIiM2QThDQUYiLCBjb2xvciA9ICJ3aGl0ZSIsIGFscGhhID0gMC44KSArICAKICBnZW9tX2RlbnNpdHkoY29sb3IgPSAiYmx1ZSIsIHNpemUgPSAwLjUpKwogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIExpZmUgRXhwZWN0YW5jeSIsIHggPSAiTGlmZSBFeHBlY3RhbmN5IChZZWFycykgIiwgeSA9ICJDb3VudCIpICsKICB0aGVtZV9taW5pbWFsKCkgKyAgCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCgpgYGAKCi0gICBCb3ggcGxvdCBvZiBMaWZlIEV4cGVjdGFuY3kgYmV0d2VlbiBncm91cCBvZiBjb3VudHJpZXM6CgogICAgLSAgIERldmVsb3BlZCBjb3VudHJpZXM6IGhpZ2hlciwgbGVzcyB2YXJpYWJpbGl0eQoKICAgIC0gICBEZXZlbG9waW5nIGNvdW50cmllczogbG93ZXIsIG1vcmUgdmFyaWFiaWxpdHkKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CiMgTGlmZSBleHBlY3RhbmN5IGJldHdlZW4gZGV2ZWxvcGVkIGFuZCBkZXZlbG9waW5nIGNvdW50cnkKZ2dwbG90KGRmLCBhZXMoeCA9IFN0YXR1cywgeSA9IExpZmUuZXhwZWN0YW5jeSwgZmlsbCA9IFN0YXR1cykpICsKICBnZW9tX2JveHBsb3QoYWxwaGEgPSAwLjcsIG91dGxpZXIuc2hhcGUgPSAxNiwgb3V0bGllci5zaXplID0gMiwgd2hpc2tlcndpZHRoID0gMSApICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiRGV2ZWxvcGVkIiA9ICIjQUVDNkNGIiwgIkRldmVsb3BpbmciID0gIiNGRkRBQzEiKSkgKyAKICBsYWJzKHRpdGxlID0gIkxpZmUgRXhwZWN0YW5jeSBpbiBEZXZlbG9wZWQgdnMuIERldmVsb3BpbmcgQ291bnRyaWVzIiwKICAgICAgIHggPSAiU3RhdHVzIiwKICAgICAgIHkgPSAiTGlmZSBFeHBlY3RhbmN5IiwKICAgICAgIGZpbGwgPSAiQ291bnRyeSBTdGF0dXMiKSArICAjIExlZ2VuZCB0aXRsZQogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsIHNpemUgPSA5KSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKYGBge3Igd2FybmluZz1GQUxTRX0KIyBGaWx0ZXIgZGF0YSBmb3IgZGV2ZWxvcGluZyBjb3VudHJpZXMKZGZfZGV2ZWxvcGluZyA8LSBkZltkZiRTdGF0dXMgPT0gIkRldmVsb3BpbmciLCBdCgojIFBsb3QgaGlzdG9ncmFtCmdncGxvdChkZl9kZXZlbG9waW5nLCBhZXMoeCA9IExpZmUuZXhwZWN0YW5jeSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDIsIGZpbGwgPSAiI0ZGREFDMSIsIGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjcpICsKICBsYWJzKHRpdGxlID0gIkhpc3RvZ3JhbSBvZiBMaWZlIEV4cGVjdGFuY3kgaW4gRGV2ZWxvcGluZyBDb3VudHJpZXMiLAogICAgICAgeCA9ICJMaWZlIEV4cGVjdGFuY3kgKFllYXJzKSIsCiAgICAgICB5ID0gIkZyZXF1ZW5jeSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpKQoKYGBgCgotICAgT3V0bGllciBhbmFseXNpczoKCmBgYHtyfQpkZXZlbG9wZWRfY291bnRyaWVzIDwtIGRmW2RmJFN0YXR1cyA9PSAiRGV2ZWxvcGluZyImIGRmJExpZmUuZXhwZWN0YW5jeTw1MCxdCmlkX2xpc3QgPC0gZGV2ZWxvcGVkX2NvdW50cmllcyRJRAoKCmBgYAoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShyZXNoYXBlMikKCiMgQ29sdW1ucyB0byBjb21wYXJlCmNvbHVtbnNfdG9fcGxvdCA8LSBjKAogICJwZXJjZW50YWdlLmV4cGVuZGl0dXJlIiwgIlRvdGFsLmV4cGVuZGl0dXJlIiwgCiAgIkdEUCIsICJJbmNvbWUuY29tcG9zaXRpb24ub2YucmVzb3VyY2VzIiwgCiAgIlNjaG9vbGluZyIsICJBbGNvaG9sIgopCgojIFN1YnNldCBkYXRhIGZvciBpZF9saXN0IGFuZCB0aGUgcmVzdCBvZiB0aGUgZGF0YQpkZXZlbG9wZWRfZGF0YSA8LSBkZltkZiRJRCAlaW4lIGlkX2xpc3QsIGNvbHVtbnNfdG9fcGxvdF0Kb3RoZXJfZGF0YSA8LSBkZlshZGYkSUQgJWluJSBpZF9saXN0LCBjb2x1bW5zX3RvX3Bsb3RdCgojIENyZWF0ZSBhIG5ldyBjb2x1bW4gdG8gaW5kaWNhdGUgdGhlIGdyb3VwcyAoaWRfbGlzdCB2cy4gcmVzdCBvZiB0aGUgZGF0YSkKZGV2ZWxvcGVkX2RhdGEkR3JvdXAgPC0gIk91dGxpZXIiCm90aGVyX2RhdGEkR3JvdXAgPC0gIk5vcm1hbCIKCiMgQ29tYmluZSBib3RoIGRhdGFzZXRzCmNvbWJpbmVkX2RhdGEgPC0gcmJpbmQoZGV2ZWxvcGVkX2RhdGEsIG90aGVyX2RhdGEpCgojIFJlc2hhcGUgdGhlIGRhdGEgZm9yIHBsb3R0aW5nCmNvbWJpbmVkX2RhdGFfbWVsdGVkIDwtIG1lbHQoY29tYmluZWRfZGF0YSwgaWQudmFycyA9ICJHcm91cCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLm5hbWUgPSAiRmVhdHVyZSIsIHZhbHVlLm5hbWUgPSAiVmFsdWUiKQoKIyBDdXN0b20gc3VicGxvdCBuYW1lcwpmZWF0dXJlX25hbWVzIDwtIGMoCiAgInBlcmNlbnRhZ2UuZXhwZW5kaXR1cmUiID0gIkhlYWx0aCBFeHBlbmRpdHVyZSAoJSkiLAogICJUb3RhbC5leHBlbmRpdHVyZSIgPSAiVG90YWwgRXhwZW5kaXR1cmUgKCUpIiwKICAiR0RQIiA9ICJHRFAiLAogICJJbmNvbWUuY29tcG9zaXRpb24ub2YucmVzb3VyY2VzIiA9ICJJbmNvbWUgQ29tcG9zaXRpb24iLAogICJTY2hvb2xpbmciID0gIlllYXJzIG9mIFNjaG9vbGluZyIsCiAgIkFsY29ob2wiID0gIkFsY29ob2wgQ29uc3VtcHRpb24iCikKCiMgQ3JlYXRlIHRoZSBib3ggcGxvdHMKZ2dwbG90KGNvbWJpbmVkX2RhdGFfbWVsdGVkLCBhZXMoeCA9IEdyb3VwLCB5ID0gVmFsdWUsIGZpbGwgPSBHcm91cCkpICsKICBnZW9tX2JveHBsb3Qob3V0bGllci5zaXplID0gMC44LCB3aWR0aCA9IDAuMykgKyAgIyBTbWFsbGVyIGJveHBsb3RzCiAgZmFjZXRfd3JhcCh+IEZlYXR1cmUsIHNjYWxlcyA9ICJmcmVlX3kiLCBuY29sID0gMywgbGFiZWxsZXIgPSBhc19sYWJlbGxlcihmZWF0dXJlX25hbWVzKSkgKyAgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiT3V0bGllciIgPSAiI0ZCQjRBRSIsICJOb3JtYWwiID0gIiNCM0NERTMiKSkgKyAgIyBQYXN0ZWwgY29sb3JzCiAgbGFicyh0aXRsZSA9ICJDb21wYXJpc29uIG9mIFNlbGVjdGVkIEZlYXR1cmVzIGJldHdlZW4gT3V0bGllcnMgYW5kIE5vcm1hbCBncm91cCIsCiAgICAgICB4PSIiLAogICAgICAgeSA9ICJWYWx1ZSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLCAgIyBTbWFsbGVyIHgtYXhpcyBsYWJlbHMKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gNyksICAjIFNtYWxsZXIgeS1heGlzIGxhYmVscwogICAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwgICMgU21hbGxlciBzdWJwbG90IHRpdGxlcwogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGFuZWwuc3BhY2luZyA9IHVuaXQoMSwgImxpbmVzIikpICAKCgpgYGAKCi0gICBDb3JyZWxhdGlvbiBtYXRyaXg6IHRvIGhhdmUgYSBmaXJzdCBvdmVydmlldyBhYm91dCBsaW5lYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gdmFyaWFibGVzCgpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTAgfQpsaWJyYXJ5KGNvcnJwbG90KQoKIyBTZWxlY3Qgb25seSBudW1lcmljIGNvbHVtbnMgKEJhc2UgUikKbnVtZXJpY19jb2xzIDwtIHNhcHBseShkZiwgaXMubnVtZXJpYykgICMgSWRlbnRpZnkgbnVtZXJpYyBjb2x1bW5zCmNvcnJfbWF0cml4IDwtIGNvcihkZlssIG51bWVyaWNfY29sc10sIHVzZSA9ICJjb21wbGV0ZS5vYnMiKSAgIyBDb21wdXRlIGNvcnJlbGF0aW9uCgojIFBsb3QgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeApjb3JycGxvdChjb3JyX21hdHJpeCwgdGwuY29sID0gImRhcmtibHVlIiwgdGwuc3J0ID0gNDUsIG1ldGhvZCA9ICJjb2xvciIpCgojIEFkZCB0aXRsZQp0aXRsZSgiQ29ycmVsYXRpb24gTWF0cml4IG9mIEZlYXR1cmVzIiwgY29sLm1haW4gPSAiYmxhY2siLCBmb250Lm1haW4gPSAyLCBjZXgubWFpbiA9IDEuNSkKYGBgCgojIyMgSW5mZXJlbmNlIEFuYWx5c2lzCgojIyMjIFBlYXJzb24gQ29ycmVsYXRpb24gQW5hbHlzaXMKCi0gICBHb2FsczogUXVhbnRpZnkgaG93IHN0cm9uZyB0aGVzZSBmZWF0dXJlcyByZWxhdGVkIHRvZ2V0aGVyIGluIGEgbGluZWFyIHJlbGF0aW9uc2hpcC4KCi0gICBJbiB0aGlzIHJlcG9ydCwgd2UgY2hvc2UgNCBmZWF0dXJlcyB3aGljaCBoYXZlIGEgcG9zaXRpdmUgZWZmZWN0IG9uIExpZmUgRXhwZWN0YW5jeTogR0RQLCBUb3RhbCBFeHBlbmRpdHVyZSwgSW5jb21lIENvbXBvc2l0aW9uIG9mIFJlc291cmNlcyBhbmQgU2Nob29saW5nLgoKICAgIGBgYHtyIHdhcm5pbmc9RkFMU0V9CiAgICBsaWJyYXJ5KGtuaXRyKQoKICAgICMgQ29tcHV0ZSBjb3JyZWxhdGlvbnMKICAgIGNvcnJlbGF0aW9ucyA8LSBkYXRhLmZyYW1lKAogICAgICBWYXJpYWJsZSA9IGMoIkdEUCIsICJUb3RhbCBFeHBlbmRpdHVyZSIsICJJbmNvbWUgQ29tcG9zaXRpb24gb2YgUmVzb3VyY2VzIiwgIlNjaG9vbGluZyIpLAogICAgICBQZWFyc29uX0NvcnJlbGF0aW9uID0gYygKICAgICAgICBjb3IoZGYkTGlmZS5leHBlY3RhbmN5LCBkZiRHRFAsIHVzZSA9ICJjb21wbGV0ZS5vYnMiLCBtZXRob2QgPSAicGVhcnNvbiIpLAogICAgICAgIGNvcihkZiRMaWZlLmV4cGVjdGFuY3ksIGRmJFRvdGFsLmV4cGVuZGl0dXJlLCB1c2UgPSAiY29tcGxldGUub2JzIiwgbWV0aG9kID0gInBlYXJzb24iKSwKICAgICAgICBjb3IoZGYkTGlmZS5leHBlY3RhbmN5LCBkZiRJbmNvbWUuY29tcG9zaXRpb24ub2YucmVzb3VyY2VzLCB1c2UgPSAiY29tcGxldGUub2JzIiwgbWV0aG9kID0gInBlYXJzb24iKSwKICAgICAgICBjb3IoZGYkTGlmZS5leHBlY3RhbmN5LCBkZiRTY2hvb2xpbmcsIHVzZSA9ICJjb21wbGV0ZS5vYnMiLCBtZXRob2QgPSAicGVhcnNvbiIpCiAgICAgICkKICAgICkKCiAgICAjIERpc3BsYXkgdGFibGUKICAgIGthYmxlKGNvcnJlbGF0aW9ucywgY29sLm5hbWVzID0gYygiVmFyaWFibGUiLCAiUGVhcnNvbiBDb3JyZWxhdGlvbiIpLCBjYXB0aW9uID0gIkNvcnJlbGF0aW9uIHdpdGggTGlmZSBFeHBlY3RhbmN5IikKCgogICAgYGBgCgpUaGUgcmVzdWx0IHNob3dzIHRoYXQ6CgotICAgTW9kZXJhdGUgcG9zaXRpdmUgY29ycmVsYXRpb24gYmV0d2VlbiBMaWZlIGV4cGVjdGFuY3kgYW5kIEdEUCA9IGhpZ2hlciBHRFAgaXMgZ2VuZXJhbGx5IGFzc29jaWF0ZWQgd2l0aCBsb25nZXIgbGlmZSBleHBlY3RhbmN5LgoKLSAgIFdlYWsgcG9zaXRpdmUgY29ycmVsYXRpb24gYmV0d2VlbiBMaWZlIEV4cGVjdGFuY3kgYW5kIFRvdGFsIEV4cGVuZGl0dXJlID0gZ292ZXJubWVudCBoZWFsdGggc3BlbmRpbmcgaGFzIGEgc21hbGwgZWZmZWN0LiBUaGlzIG1pZ2h0IGJlIGJlY2F1c2UgZmFjdG9ycyBsaWtlIGVmZmljaWVuY3kgb2Ygc3BlbmRpbmcgbWF0dGVyIG1vcmUuCgotICAgU3Ryb25nIHBvc2l0aXZlIGNvcnJlbGF0aW9uIGJldHdlZW4gTGlmZSBFeHBlY3RhbmN5IGFuZCBJbmNvbWUgQ29tcG9zaXRpb24gb2YgUmVzb3VyY2VzID0gYSBjb3VudHJ54oCZcyBlY29ub21pYyBlcXVhbGl0eSAoaHVtYW4gZGV2ZWxvcG1lbnQgZmFjdG9ycyBsaWtlIGVkdWNhdGlvbiwgaW5jb21lIGRpc3RyaWJ1dGlvbikgaGFzIGEgc2lnbmlmaWNhbnQgZWZmZWN0IG9uIGxpZmUgZXhwZWN0YW5jeS4KCiMjIyMgTXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwKCi0gICBVc2luZyBwLXZhbHVlIHdpbGwgaGVscCB1cyBpbiBleGNsdWRlIHZhcmlhYmxlcyB0aGF0IGRvIG5vdCBjb250cmlidXRlIHNpZ25pZmljYW50bHkgYW5kIGZvY3VzIG9uIHRoZSBtb3N0IGltcG9ydGFudCBwcmVkaWN0b3JzCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpkZiRJbmNvbWUuY29tcG9zaXRpb24ubm9ybSA8LSBzY2FsZShkZiRJbmNvbWUuY29tcG9zaXRpb24ub2YucmVzb3VyY2VzKQptb2RlbCA8LSBsbShMaWZlLmV4cGVjdGFuY3kgfiBHRFAgKyBUb3RhbC5leHBlbmRpdHVyZSArIEluY29tZS5jb21wb3NpdGlvbi5ub3JtICsgU2Nob29saW5nLCBkYXRhID0gZGYpCnN1bW1hcnkobW9kZWwpCmBgYAoKVGhlIHJlc3VsdCBzaG93IHRoYXQ6CgotICAgTXVsdGlwbGUgUi1zcXVhcmVkID0gMC42MDE3OiB0aGUgbW9kZWwgZXhwbGFpbnMgNjAuMTclIG9mIHRoZSB2YXJpYW5jZSBpbiBsaWZlIGV4cGVjdGFuY3kKCi0gICBGLXN0YXRpc3RpYyA9IDYwMy4xLCBwIFw8IDIuMmUtMTY6IHRoZSBtb2RlbCBpcyBoaWdobHkgc2lnbmlmaWNhbnQsIG1lYW5pbmcgYXQgbGVhc3Qgb25lIHByZWRpY3RvciBzdHJvbmdseSBhZmZlY3RzIGxpZmUgZXhwZWN0YW5jeS4KCi0gICBwLXZhbHVlOiBHRFAgYW5kIEluY29tZSBjb21wb3NpdGlvbiBhcmUgc2lnbmlmaWNhbnQgZWZmZWN0IGluIGxpZmUgZXhwZWN0YW5jeSAocC12YWx1ZSBcPDAuMDAxKS4gRXNwZWNpYWxseSBJbmNvbWUgY29tcG9zaXRpb24gd2l0aCAxIHVuaXQgaW5jcmVhc2UgaW4gdGhlIHZhcmlhYmxlIHJlc3VsdHMgaW4gYSAzLjMteWVhciBpbmNyZWFzZSBpbiBsaWZlIGV4cGVjdGFuY3kuIE9uIHRoZSBvdGhlciBoYW5kLCBUb3RhbCBleHBlbmRpdHVyZSBoYXMgbm8gc2lnbmlmaWNhbnQgZWZmZWN0IG9uIGxpZmUgZXhwZWN0YW5jeSBhdCA1JSBsZXZlbCAocC12YWx1ZSBcPiAwLjA1KQoKKipSZWxhdGlvbnNoaXAgYmV0d2VlbiBmZWF0dXJlcyoqCgpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9CiMgTG9hZCBuZWNlc3NhcnkgbGlicmFyeQpsaWJyYXJ5KGdncGxvdDIpCgojIERlZmluZSBjdXN0b20gcGFzdGVsIGNvbG9ycwpwb2ludF9jb2xvciA8LSAiIzU2QjRFOSIgICMgUGFzdGVsIGJsdWUgZm9yIHBvaW50cwpsaW5lX2NvbG9yIDwtICIjRTY5RjAwIiAgICMgUGFzdGVsIG9yYW5nZSBmb3IgcmVncmVzc2lvbiBsaW5lCmZpbGxfY29sb3IgPC0gIiNFNjlGMDAiICAgIyBMaWdodCBwYXN0ZWwgb3JhbmdlIGZvciBjb25maWRlbmNlIGludGVydmFsCgojIEdEUCB2cy4gTGlmZSBFeHBlY3RhbmN5IChMaW5lYXIgUmVncmVzc2lvbikKcDEgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IEdEUCwgeSA9IExpZmUuZXhwZWN0YW5jeSkpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC41LCBjb2xvciA9IHBvaW50X2NvbG9yKSArICAjIFRyYW5zcGFyZW50IHBhc3RlbCBibHVlIHBvaW50cwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbG9yID0gbGluZV9jb2xvciwgZmlsbCA9IGZpbGxfY29sb3IsIGFscGhhID0gMC4zKSArICAjIFJlZ3Jlc3Npb24gbGluZSB3aXRoIHNoYWRpbmcKICBsYWJzKHRpdGxlID0gIkdEUCB2cy4gTGlmZSBFeHBlY3RhbmN5IiwKICAgICAgIHggPSAiR0RQIHBlciBDYXBpdGEiLAogICAgICAgeSA9ICJMaWZlIEV4cGVjdGFuY3kiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDEzKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgojIFRvdGFsIEV4cGVuZGl0dXJlIHZzLiBMaWZlIEV4cGVjdGFuY3kgKExpbmVhciBSZWdyZXNzaW9uKQpwMiA8LSBnZ3Bsb3QoZGYsIGFlcyh4ID0gVG90YWwuZXhwZW5kaXR1cmUsIHkgPSBMaWZlLmV4cGVjdGFuY3kpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSwgY29sb3IgPSBwb2ludF9jb2xvcikgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbG9yID0gbGluZV9jb2xvciwgZmlsbCA9IGZpbGxfY29sb3IsIGFscGhhID0gMC4zKSArCiAgbGFicyh0aXRsZSA9ICJIZWFsdGhjYXJlIEV4cGVuZGl0dXJlIHZzLiBMaWZlIEV4cGVjdGFuY3kiLAogICAgICAgeCA9ICJUb3RhbCBIZWFsdGhjYXJlIEV4cGVuZGl0dXJlIiwKICAgICAgIHkgPSAiTGlmZSBFeHBlY3RhbmN5IikgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLCBzaXplID0gMTMpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiMgSW5jb21lIENvbXBvc2l0aW9uIG9mIFJlc291cmNlcyB2cy4gTGlmZSBFeHBlY3RhbmN5IChMaW5lYXIgUmVncmVzc2lvbikKcDMgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IEluY29tZS5jb21wb3NpdGlvbi5ub3JtLCB5ID0gTGlmZS5leHBlY3RhbmN5KSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUsIGNvbG9yID0gcG9pbnRfY29sb3IpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBjb2xvciA9IGxpbmVfY29sb3IsIGZpbGwgPSBmaWxsX2NvbG9yLCBhbHBoYSA9IDAuMykgKwogIGxhYnModGl0bGUgPSAiSW5jb21lIENvbXBvc2l0aW9uIHZzLiBMaWZlIEV4cGVjdGFuY3kiLAogICAgICAgeCA9ICJJbmNvbWUgQ29tcG9zaXRpb24gb2YgUmVzb3VyY2VzIiwKICAgICAgIHkgPSAiTGlmZSBFeHBlY3RhbmN5IikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxMyksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQojIFNjaG9vbGluZyB2cy4gTGlmZSBFeHBlY3RhbmN5IChMaW5lYXIgUmVncmVzc2lvbikKcDQgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IFNjaG9vbGluZywgeSA9IExpZmUuZXhwZWN0YW5jeSkpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC41LCBjb2xvciA9IHBvaW50X2NvbG9yKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3IgPSBsaW5lX2NvbG9yLCBmaWxsID0gZmlsbF9jb2xvciwgYWxwaGEgPSAwLjMpICsKICBsYWJzKHRpdGxlID0gIlNjaG9vbGluZyB2cy4gTGlmZSBFeHBlY3RhbmN5IiwKICAgICAgIHggPSAiU2Nob29saW5nIiwKICAgICAgIHkgPSAiTGlmZSBFeHBlY3RhbmN5IikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxMyksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKIyBEaXNwbGF5IHBsb3RzIHRvZ2V0aGVyCmxpYnJhcnkoZ3JpZEV4dHJhKQpncmlkLmFycmFuZ2UocDEsIHAyLCBwMyxwNCwgbmNvbCA9IDEpICAjIEFycmFuZ2UgcGxvdHMgaW4gYSBzaW5nbGUgcm93CmBgYAoKIyMjIFBlcmZvcm0gU3BhdGlhbCBDbHVzdGVyaW5nIHdpdGggTW9yYW7igJlzIEkgVGVzdAoKLSAgIE1vcmFu4oCZcyBJIHN0YXRpc3RpYyBtZWFzdXJlcyBob3cgc2ltaWxhciBuZWlnaGJvcmluZyBvYnNlcnZhdGlvbnMgYXJlLgoKLSAgIFJlc3VsdCBpbnRlcnByZXRhdGlvbjoKCiAgICAtICAgKipwLXZhbHVlIFw8IDAuMDAxKiog4oaSICoqU3RhdGlzdGljYWxseSBzaWduaWZpY2FudCoqLCBtZWFuaW5nIGxpZmUgZXhwZWN0YW5jeSBpcyAqKm5vdCByYW5kb21seSBkaXN0cmlidXRlZCoqIGJ1dCBmb2xsb3dzIGEgc3BhdGlhbCBwYXR0ZXJuCgogICAgLSAgICoqTW9yYW7igJlzIEkgPSAwLjkyMSoqIOKGkiBTdHJvbmcgKipwb3NpdGl2ZSBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbioqIChoaWdoL2xvdyBsaWZlIGV4cGVjdGFuY3kgcmVnaW9ucyBjbHVzdGVyIHRvZ2V0aGVyKS4KCmBgYHtyIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkobGVhZmxldCkKbGlicmFyeShzZikKbGlicmFyeShybmF0dXJhbGVhcnRoKQpsaWJyYXJ5KHJuYXR1cmFsZWFydGhkYXRhKQpsaWJyYXJ5KHNwZGVwKSAgIyBGb3Igc3BhdGlhbCBjbHVzdGVyaW5nCmxpYnJhcnkoZHBseXIpCiMgQ29udmVydCBkYXRhZnJhbWUgdG8gYW4gc2Ygc3BhdGlhbCBvYmplY3QKZGZfc2YgPC0gc3RfYXNfc2YoZGYsIGNvb3JkcyA9IGMoImxvbmdpdHVkZSIsICJsYXRpdHVkZSIpLCBjcnMgPSA0MzI2KQoKIyBTbGlnaHRseSBqaXR0ZXIgY29vcmRpbmF0ZXMgdG8gYXZvaWQgaWRlbnRpY2FsIHBvaW50cyBpc3N1ZQpzZXQuc2VlZCg0MikKZGZfc2YkZ2VvbWV0cnkgPC0gc3Rfaml0dGVyKGRmX3NmJGdlb21ldHJ5LCBhbW91bnQgPSAxZS01KQoKIyBMb2FkIHdvcmxkIG1hcCBkYXRhCndvcmxkIDwtIG5lX2NvdW50cmllcyhzY2FsZSA9ICJtZWRpdW0iLCByZXR1cm5jbGFzcyA9ICJzZiIpICAjIEtlZXAgc2VwYXJhdGUhCgojIERlZmluZSBjb2xvciBwYWxldHRlIGJhc2VkIG9uIExpZmUgRXhwZWN0YW5jeQpwYWwgPC0gY29sb3JOdW1lcmljKHBhbGV0dGUgPSAidmlyaWRpcyIsIGRvbWFpbiA9IGRmX3NmJExpZmUuZXhwZWN0YW5jeSkKCiMgQ3JlYXRlIHNwYXRpYWwgbmVpZ2hib3JzIHVzaW5nIGstbmVhcmVzdCBuZWlnaGJvcnMKY29vcmRzIDwtIHN0X2Nvb3JkaW5hdGVzKGRmX3NmKQpuZWlnaGJvcnMgPC0ga25lYXJuZWlnaChjb29yZHMsIGsgPSA1KQpuYiA8LSBrbm4ybmIobmVpZ2hib3JzKQoKIyBDb252ZXJ0IHRvIHNwYXRpYWwgd2VpZ2h0cwp3ZWlnaHRzIDwtIG5iMmxpc3R3KG5iLCBzdHlsZSA9ICJXIikKCiMgQ29tcHV0ZSBNb3JhbidzIEkgdGVzdCBmb3Igc3BhdGlhbCBjbHVzdGVyaW5nCm1vcmFuc190ZXN0IDwtIG1vcmFuLnRlc3QoZGZfc2YkTGlmZS5leHBlY3RhbmN5LCBsaXN0dyA9IHdlaWdodHMpCgpwcmludChtb3JhbnNfdGVzdCkgICMgQ2hlY2sgcC12YWx1ZSB0byBjb25maXJtIGNsdXN0ZXJpbmcKCgpgYGAKCi0gICBJbnRlcmFjdGl2ZSBTcGF0aWFsIE1hcHBpbmc6IHdlIGNhbiBzZWUgY2xlYXJseSB0aGUgY2x1c3RlciBvZiBjb3VudHJ5IHdpdGggaGlnaCBhbmQgbG93IGxpZmUgZXhwZWN0YW5jeQoKYGBge3Igd2FybmluZz1GQUxTRX0KIyBDcmVhdGUgYW4gaW50ZXJhY3RpdmUgbGVhZmxldCBtYXAKbGVhZmxldCgpICU+JQogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJENhcnRvREIuUG9zaXRyb24pICU+JSAgIyBBZGQgYmFzZW1hcAoKICAjIEFkZCB3b3JsZCBtYXAgYXMgYSBiYWNrZ3JvdW5kIGxheWVyIChvcHRpb25hbCkKICBhZGRQb2x5Z29ucyhkYXRhID0gd29ybGQsIGZpbGxPcGFjaXR5ID0gMC4xLCBjb2xvciA9ICJncmF5Iiwgd2VpZ2h0ID0gMSkgJT4lCgogICMgQWRkIGxpZmUgZXhwZWN0YW5jeSBwb2ludHMKICBhZGRDaXJjbGVNYXJrZXJzKAogICAgZGF0YSA9IGRmX3NmLAogICAgcmFkaXVzID0gNSwKICAgIGNvbG9yID0gfnBhbChMaWZlLmV4cGVjdGFuY3kpLAogICAgc3Ryb2tlID0gRkFMU0UsCiAgICBmaWxsT3BhY2l0eSA9IDAuOCwKICAgIHBvcHVwID0gfnBhc3RlMCgiQ291bnRyeTogIiwgQ291bnRyeSwgIjxicj4iLAogICAgICAgICAgICAgICAgICAgICJMaWZlIEV4cGVjdGFuY3k6ICIsIHJvdW5kKExpZmUuZXhwZWN0YW5jeSwgMikpCiAgKSAlPiUKCiAgIyBBZGQgbGVnZW5kCiAgYWRkTGVnZW5kKAogICAgImJvdHRvbXJpZ2h0IiwKICAgIHBhbCA9IHBhbCwKICAgIHZhbHVlcyA9IGRmX3NmJExpZmUuZXhwZWN0YW5jeSwKICAgIHRpdGxlID0gIkxpZmUgRXhwZWN0YW5jeSIsCiAgICBvcGFjaXR5ID0gMQogICkgJT4lCiAgYWRkU2NhbGVCYXIocG9zaXRpb24gPSAiYm90dG9tbGVmdCIpCmBgYAoKIyBNYWNoaW5lIExlYXJuaW5nIGZvciBTcGF0aWFsIFByZWRpY3Rpb24KCi0gICBHb2FsczogdG8gcHJlZGljdCBoaWdoLXJpc2sgcmVnaW9ucyAocmVnaW9ucyB3aGVyZSBsaWZlIGV4cGVjdGFuY3kgaXMgaW4gdGhlIGJvdHRvbSAyNSUpIGJhc2VkIG9uIGdlb2dyYXBoaWNhbCBhbmQgc29jaW9lY29ub21pYyBpbmZvcm1hdGlvbi4gVGhpcyBpcyBmcmFtZWQgYXMgYSBiaW5hcnkgY2xhc3NpZmljYXRpb24gcHJvYmxlbSwgd2hlcmU6CgogICAgLSAgIDEgPSBoaWdoLXJpc2sgcmVnaW9uIChsb3cgbGlmZSBleHBlY3RhbmN5KS4KCiAgICAtICAgMCA9IGxvdy1yaXNrIHJlZ2lvbiAoaGlnaGVyIGxpZmUgZXhwZWN0YW5jeSkuCgotICAgTWV0aG9kb2xvZ3k6IFVzaW5nIFJhbmRvbSBGb3Jlc3QgbW9kZWwgd2hpY2ggY2FuIGhhbmRsZXMgbm9uLWxpbmVhciByZWxhdGlvbnNoaXBzIGRhdGEuIFNpbmNlIGxpZmUgZXhwZWN0YW5jeSBjYW4gaGF2ZSBub24tbGluZWFyIGRlcGVuZGVuY2llcyBvbiBvdGhlciBmZWF0dXJlcy4KCi0gICBFdmFsdWF0aW9uOiB1c2luZyBST0MgY3VydmUKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoY2FyZXQpICAgICAgICAgICAjIE1hY2hpbmUgbGVhcm5pbmcgdXRpbGl0aWVzCmxpYnJhcnkocmFuZG9tRm9yZXN0KSAgICAjIFJhbmRvbSBGb3Jlc3QgbW9kZWwKbGlicmFyeShnZ3Bsb3QyKSAgICAgICAgICMgVmlzdWFsaXphdGlvbgpsaWJyYXJ5KHNmKSAgICAgICAgICAgICAgIyBTcGF0aWFsIGRhdGEgaGFuZGxpbmcKbGlicmFyeShkcGx5cikgICAgICAgICAgICMgRGF0YSBtYW5pcHVsYXRpb24KCiMgRGVmaW5lIEhpZ2gtUmlzayBSZWdpb25zIChCb3R0b20gMjUlIExpZmUgRXhwZWN0YW5jeSkKdGhyZXNob2xkIDwtIHF1YW50aWxlKGRmJExpZmUuZXhwZWN0YW5jeSwgMC4yNSwgbmEucm0gPSBUUlVFKSAKZGYkSGlnaFJpc2sgPC0gaWZlbHNlKGRmJExpZmUuZXhwZWN0YW5jeSA8IHRocmVzaG9sZCwgMSwgMCkgCgojIEVuc3VyZSBIaWdoUmlzayBpcyBhIEZhY3RvcgpkZiRIaWdoUmlzayA8LSBmYWN0b3IoZGYkSGlnaFJpc2ssIGxldmVscyA9IGMoMCwgMSkpCgojIFNlbGVjdCBGZWF0dXJlcyBmb3IgUHJlZGljdGlvbgpkZl9tbCA8LSBzdWJzZXQoZGYsIHNlbGVjdCA9IGMoSGlnaFJpc2ssIEdEUCwgcGVyY2VudGFnZS5leHBlbmRpdHVyZSwgUG9wdWxhdGlvbiwgU2Nob29saW5nLCBJbmNvbWUuY29tcG9zaXRpb24ub2YucmVzb3VyY2VzLCBsYXRpdHVkZSwgbG9uZ2l0dWRlKSkKCgojIFRyYWluLVRlc3QgU3BsaXQgKDgwJSBUcmFpbiwgMjAlIFRlc3QpCnNldC5zZWVkKDQyKQp0cmFpbkluZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oZGZfbWwkSGlnaFJpc2ssIHAgPSAwLjgsIGxpc3QgPSBGQUxTRSkKdHJhaW5EYXRhIDwtIGRmX21sW3RyYWluSW5kZXgsIF0KdGVzdERhdGEgPC0gZGZfbWxbLXRyYWluSW5kZXgsIF0KCiMgVHJhaW4gUmFuZG9tIEZvcmVzdCBNb2RlbApyZl9tb2RlbCA8LSByYW5kb21Gb3Jlc3QoSGlnaFJpc2sgfiAuLCBkYXRhID0gdHJhaW5EYXRhLCBudHJlZSA9IDUwMCwgbXRyeSA9IDMsIGltcG9ydGFuY2UgPSBUUlVFKQoKIyBNb2RlbCBFdmFsdWF0aW9uIG9uIFRlc3QgRGF0YQpwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KHJmX21vZGVsLCB0ZXN0RGF0YSkKCiMgRW5zdXJlIHByZWRpY3Rpb25zIGFuZCB0ZXN0IGxhYmVscyBoYXZlIHNhbWUgZmFjdG9yIGxldmVscwp0ZXN0RGF0YSRIaWdoUmlzayA8LSBmYWN0b3IodGVzdERhdGEkSGlnaFJpc2ssIGxldmVscyA9IGMoMCwgMSkpCnByZWRpY3Rpb25zIDwtIGZhY3RvcihwcmVkaWN0aW9ucywgbGV2ZWxzID0gYygwLCAxKSkKCiMgQ29tcHV0ZSBDb25mdXNpb24gTWF0cml4CmNvbmZfbWF0cml4IDwtIGNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9ucywgdGVzdERhdGEkSGlnaFJpc2spCnByaW50KGNvbmZfbWF0cml4KSAgIyBBY2N1cmFjeSwgUHJlY2lzaW9uLCBSZWNhbGwKCiMgRmVhdHVyZSBJbXBvcnRhbmNlCmltcG9ydGFuY2UocmZfbW9kZWwpCnZhckltcFBsb3QocmZfbW9kZWwpCgojIENoZWNrIGlmIGJvdGggY2xhc3NlcyBhcmUgcHJlZGljdGVkCnRhYmxlKHByZWRpY3Rpb25zKQp0YWJsZSh0ZXN0RGF0YSRIaWdoUmlzaykKCiMgVmlzdWFsaXphdGlvbjogTWFwIG9mIFByZWRpY3RlZCBIaWdoLVJpc2sgUmVnaW9ucwpkZl9zZiA8LSBzdF9hc19zZihkZiwgY29vcmRzID0gYygibG9uZ2l0dWRlIiwgImxhdGl0dWRlIiksIGNycyA9IDQzMjYpICMgQ29udmVydCB0byBTcGF0aWFsIE9iamVjdApkZl9zZiRQcmVkaWN0ZWRfUmlzayA8LSBwcmVkaWN0KHJmX21vZGVsLCBkZl9tbCkKCgpgYGAKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CmNtIDwtIGNvbmZfbWF0cml4JHRhYmxlCiMgVHJ1ZSBQb3NpdGl2ZXMgKFRQKSwgRmFsc2UgTmVnYXRpdmVzIChGTiksIEZhbHNlIFBvc2l0aXZlcyAoRlApLCBhbmQgVHJ1ZSBOZWdhdGl2ZXMgKFROKQpUUCA8LSBjbVsyLCAyXSAgIyBIaWdoLVJpc2sgY29ycmVjdGx5IHByZWRpY3RlZCBhcyBIaWdoLVJpc2sKRk4gPC0gY21bMSwgMl0gICMgTm9uLUhpZ2gtUmlzayBpbmNvcnJlY3RseSBwcmVkaWN0ZWQgYXMgSGlnaC1SaXNrCkZQIDwtIGNtWzIsIDFdICAjIEhpZ2gtUmlzayBpbmNvcnJlY3RseSBwcmVkaWN0ZWQgYXMgTm9uLUhpZ2gtUmlzawpUTiA8LSBjbVsxLCAxXSAgIyBOb24tSGlnaC1SaXNrIGNvcnJlY3RseSBwcmVkaWN0ZWQgYXMgTm9uLUhpZ2gtUmlzawoKIyBUcnVlIFBvc2l0aXZlIFJhdGUgKFRQUikgYW5kIEZhbHNlIFBvc2l0aXZlIFJhdGUgKEZQUikKVFBSIDwtIFRQIC8gKFRQICsgRk4pICAjIFRydWUgUG9zaXRpdmUgUmF0ZQpGUFIgPC0gRlAgLyAoRlAgKyBUTikgICMgRmFsc2UgUG9zaXRpdmUgUmF0ZQoKIyBQcmludCBUUFIgYW5kIEZQUgpjYXQoIlRydWUgUG9zaXRpdmUgUmF0ZSAoVFBSKToiLCBUUFIsICJcbiIpCmNhdCgiRmFsc2UgUG9zaXRpdmUgUmF0ZSAoRlBSKToiLCBGUFIsICJcbiIpCmBgYAoKYGBge3J9CmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGEgPSBkZl9zZiwgYWVzKGNvbG9yID0gYXMuZmFjdG9yKFByZWRpY3RlZF9SaXNrKSkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiI0IzQ0RFMyIsICIjRkZCM0JBIiksIGxhYmVscyA9IGMoIkxvdy1SaXNrIiwiSGlnaC1SaXNrIikpICsKICBsYWJzKHRpdGxlID0gIlByZWRpY3RlZCBIaWdoLVJpc2sgUmVnaW9ucyIsIGNvbG9yID0gIlJpc2sgTGV2ZWwiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZSgKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiksIAoKICApCmBgYAoKYGBge3Igd2FybmluZz1GQUxTRX0KbGlicmFyeShwUk9DKSAgCnByZWRfcHJvYnMgPC0gcHJlZGljdChyZl9tb2RlbCwgdGVzdERhdGEsIHR5cGUgPSAicHJvYiIpWywyXSAgIyBQcm9iYWJpbGl0aWVzIGZvciBIaWdoLVJpc2sgY2xhc3MgKDEpCgojIENvbXB1dGUgUk9DIEN1cnZlCnJvY19vYmogPC0gcm9jKHRlc3REYXRhJEhpZ2hSaXNrLCBwcmVkX3Byb2JzKQoKIyBQbG90IFJPQyBDdXJ2ZQpnZ3Bsb3QoZGF0YSA9IGRhdGEuZnJhbWUoVFBSID0gcm9jX29iaiRzZW5zaXRpdml0aWVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgIEZQUiA9IDEgLSByb2Nfb2JqJHNwZWNpZmljaXRpZXMpKSArCiAgZ2VvbV9saW5lKGFlcyh4ID0gRlBSLCB5ID0gVFBSKSwgY29sb3IgPSAiI0NEQjdGNiIsIHNpemUgPSAxKSArCiAgZ2VvbV9hYmxpbmUoc2xvcGUgPSAxLCBpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmV5IikgKwogIGxhYnModGl0bGUgPSBwYXN0ZSgiUk9DIEN1cnZlIiksCiAgICAgICB4ID0gIkZhbHNlIFBvc2l0aXZlIFJhdGUgIiwKICAgICAgIHkgPSAiVHJ1ZSBQb3NpdGl2ZSBSYXRlICIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpSZXN1bHQgaW50ZXJwcmV0YXRpb246CgotICAgVGhlIFJPQyBjdXJ2ZSBzdGF5IGF3YXkgZnJvbSB0aGUgcmFuZG9tIGxpbmUgbW9kZWwg4oCTXD4gZ29vZCBjbGFzc2lmaWNhdGlvbiBtb2RlbC4KCiMgQ29uY2x1c2lvbgoKLSAgIEhhdmluZyBhbiBvdmVydmlldyBhYm91dCBMaWZlIEV4cGVjdGFuY3kgYmV0d2VlbiB2YXJpb3VzIHJlZ2lvbiBhbmQgd2hpY2ggZmFjdG9yIGNhbiBpbXBhY3QgaXQsIHVzaW5nIG11bHRpcGxlcyBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIE1ldGhvZCBhbmQgU3BhdGlhbCBBbmFseXNpcyBNZXRob2QuCgotICAgQWRhcHRpbmcgYSBtYWNoaW5lIGxlYXJuaW5nIG1vZGVsIHRvIGJ1aWxkaW5nIGEgY2xhc3NpZmllciBmb3IgaGlnaC1yaXNrIHJlZ2lvbiwgdXNpbmcgZ2VvZ3JhcGhpY2FsIGFuZCBzb2Npb2Vjb25vbWljIGZlYXR1cmVzLiBUaGVzZSBtb2RlbCBjYW4gYmUgaGVscGZ1bCBpbiBkZXRlcm1pbmluZyBzdHJhdGVneSBmb3IgaW1wcm92aW5nIExpZmUgZXhwZWN0YW5jeSB3aXRoaW4gY291bnRyaWVzLgo=